{
  "openapi": "3.0.0",
  "info": {
    "title": "Nurama API",
    "version": "0.3.3",
    "description": "REST API for the Nurama platform. All endpoints are served under `/v1`.\n\nAuthenticate with a user JWT (`Authorization: Bearer <token>`) against `api.nurama.com`, or with a bot API key\n(`nrm_bot_…`) against `bot.nurama.com`. Only success and route-specific error responses are documented for each\noperation; `400` (validation), `401` (unauthenticated) and `403` (forbidden) can be returned by any authenticated\nendpoint and share the `Error` schema.\n\nReal-time events are delivered over Socket.IO and documented separately as an AsyncAPI document."
  },
  "servers": [
    {
      "url": "https://api.nurama.com/v1",
      "description": "Production"
    },
    {
      "url": "https://bot.nurama.com/v1",
      "description": "Production (bot API keys)"
    }
  ],
  "paths": {
    "/assets/{assetId}/access-activity": {
      "post": {
        "summary": "Record a play event from an authenticated player",
        "description": "Reports a play event from an authenticated player. Events are\nde-duplicated per UTC day per session, so resuming from pause or\nreplaying a region within one session counts once. `download` and\n`embed_resolved` are NOT accepted on this route — those are recorded\nby the API automatically to keep the counts authoritative.\n",
        "tags": [
          "AccessActivity"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "enum": [
                      "play_started",
                      "play_completed"
                    ]
                  },
                  "visibility": {
                    "type": "string",
                    "enum": [
                      "creator",
                      "reviewer"
                    ],
                    "description": "Audience side the play happened on. `public` is reserved\nfor the unauthenticated /public-download/{token} route.\n"
                  }
                },
                "required": [
                  "eventType",
                  "visibility"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Event recorded."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Read aggregated access activity for one asset",
        "description": "Returns totals per eventType, a top-N breakdown for one dimension,\nand a zero-filled daily series for the requested eventType. Gated\nby `canGetAccessActivity` — granted to the same roles that hold\n`canGetPublicLinks`.\n",
        "tags": [
          "AccessActivity"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "range",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "30d",
              "enum": [
                "7d",
                "30d",
                "90d"
              ]
            }
          },
          {
            "name": "groupBy",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "visibility",
              "enum": [
                "visibility",
                "referrerHost",
                "country",
                "userAgentClass",
                "day"
              ]
            }
          },
          {
            "name": "eventType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "play_started",
                "play_completed",
                "download",
                "embed_resolved"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregated stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "range": {
                      "type": "string",
                      "example": "30d"
                    },
                    "from": {
                      "type": "string",
                      "example": "2026-04-13"
                    },
                    "to": {
                      "type": "string",
                      "example": "2026-05-13"
                    },
                    "totals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "eventType": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "breakdown": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string",
                            "description": "The dimension value. Empty string represents\n\"Unknown\" (see `label` for display).\n"
                          },
                          "label": {
                            "type": "string",
                            "example": "Unknown"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "series": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "example": "2026-05-13"
                          },
                          "eventType": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "groupBy": {
                      "type": "string"
                    },
                    "eventType": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public-download/{token}/access-activity": {
      "post": {
        "summary": "Record a play event from an embedded player (unauthenticated)",
        "description": "Reports a play event from an embedded (unauthenticated) player. The\ntoken is the 10-character public-link token and is the only\ncredential — no bearer token is used. Events on this route are\nalways recorded with `visibility: 'public'`; the body accepts no\n`visibility` field. Only recorded while the link is `active`: a\nmissing or disabled link is rejected with 404 (deliberately\nindistinguishable), an expired one with 410.\n",
        "tags": [
          "AccessActivity",
          "PublicAssetLinks"
        ],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "Ab3dEf9HiJ"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "enum": [
                      "play_started",
                      "play_completed"
                    ]
                  }
                },
                "required": [
                  "eventType"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Event recorded."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Token not found or link not active (`publicAssetLinkNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Link expired (`publicAssetLinkExpired`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/access-activity/top-assets": {
      "get": {
        "summary": "Top-N assets in a project by access count",
        "description": "Returns `(assetId, count)` pairs ordered by total count of the\nrequested `eventType` in the range. Only ids and counts are\nreturned; fetch names and thumbnails through the asset endpoints.\nRequires `canGetAccessActivity`.\n",
        "tags": [
          "AccessActivity"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "range",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "30d",
              "enum": [
                "7d",
                "30d",
                "90d"
              ]
            }
          },
          {
            "name": "eventType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "play_started",
              "enum": [
                "play_started",
                "play_completed",
                "download",
                "embed_resolved"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Top-N assets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "range": {
                      "type": "string"
                    },
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "eventType": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "assetId": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/ai/polish": {
      "post": {
        "summary": "Polish a draft message in the requested tone.",
        "description": "Rewrites the user's draft message according to the requested tone while\npreserving meaning, intent, and any technical terms. In the Nurama web\napp this backs the chat composer's \"Polish with Nu\" button.\n\nReturns the rewritten text plus the credits actually billed and the\nworkspace's balance after deduction.\n\nCaps:\n  - `text` is capped at 4000 characters (~1000 input tokens).\n  - Output is capped at ~600 tokens server-side.\n\nIf the workspace has an `aiCustomPreprompt` configured it is prepended\nas additional system context (workspace-specific guidance to the model).\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workspace whose credits will be billed and whose AI settings gate the call."
                  },
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4000,
                    "description": "The user's draft message to be rewritten."
                  },
                  "toneId": {
                    "type": "string",
                    "enum": [
                      "professional",
                      "casual",
                      "concise",
                      "friendly",
                      "formal",
                      "cleanup"
                    ],
                    "description": "One of the IDs returned by `GET /ai/tones`."
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional project context, recorded on the usage event for reporting."
                  }
                },
                "required": [
                  "workspaceId",
                  "text",
                  "toneId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft polished successfully. Credits have been deducted and a usage event recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiPolishResponse"
                },
                "examples": {
                  "ok": {
                    "summary": "Successful polish",
                    "value": {
                      "polishedText": "Could you please send me the file when you have a moment? Thank you.",
                      "billedCredits": 3,
                      "balanceAfter": 9997,
                      "eventId": "0193e4a1-b2c3-7000-8000-00000000abcd"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or a gate / tool error. Possible `type` values:\n  - `validationError` — the request body failed validation\n  - `aiToneNotFound` — `toneId` not in the catalogue\n  - `aiDraftEmpty` — `text` empty or whitespace-only\n  - `aiDraftTooLong` — `text` exceeds the 4000-character cap\n  - `subscriptionNotActive` — the workspace has no active subscription\n  - `creditBalanceInsufficient` — workspace balance is below the\n    worst-case estimate (`errorData: { creditsRequired, currentBalance }`)\n  - `creditOrderExceedsUserDailySpendLimit` — the caller has hit\n    their daily AI spend cap (`errorData: { creditsRequired, userDailySpend, userDailySpendBudget }`)\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. Possible `type` values:\n  - `forbidden` — the caller does not hold `canAiPolishInput` on the workspace.\n  - `capabilityNotAvailable` — the workspace's plan lacks the `ai`\n    capability or `aiAddOnEnabled` is off (`errorData: { capability: 'ai', workspaceId }`).\n  - `aiFeatureNotEnabled` — effective AI settings (project\n    overrides workspace) have `allowAiFeatures` or `aiPolishEnabled`\n    off, or the caller is a reviewer and `aiPolishAllowReviewer` is off.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The AI provider call failed. `type` is `aiProviderFailure` or\n`aiProviderEmptyResponse`. No credits are deducted; no event is recorded.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/ai/task-generation": {
      "post": {
        "summary": "Convert a chat message into one or more board-task drafts.",
        "description": "Generates a list of `{ subject, description }` task drafts from a\nchat message. In the Nurama web app this backs the \"Generate with Nu\"\noption in a message's create-task menu; the returned `tasks` array\nseeds the task creator's draft list.\n\n**Compound capability requirement.** Unlike Polish, this endpoint\nrequires BOTH the `boards` capability (checked against the project)\nand the `ai` capability (checked against the workspace); both\n`workspaceId` and `projectId` must belong to an active subscription.\nEither capability missing returns `capabilityNotAvailable`.\n\n**Context window.** `contextMessages` is the preceding chat\nthread (oldest first). The server caps at 10 messages and each\nentry's `content` at 8000 characters; supply the messages immediately\nbefore the focal one so the model can resolve pronouns and references.\n\n**Permission.** Requires `canAiGenerateTasks` (held by all\nworkspace/project admins and by the creator, reviewer and\nreviewerAdmin roles).\n\n**Pre-prompt.** If the workspace or project has\n`aiTaskGenerationPreprompt` configured, it is appended to the\nsystem prompt as an override block (project value wins per-key).\nSeparate from `aiCustomPreprompt` so admins can tune task style\nwithout affecting Polish or Chat output.\n\nCaps:\n  - `messageText` is capped at 8000 characters.\n  - `contextMessages` is capped at 10 entries; each entry's\n    `content` is capped at 8000 characters and `authorName` at 100.\n  - At most 10 tasks are returned.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workspace whose credits will be billed and whose AI settings gate the call."
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Project the generated tasks will belong to. Required: the\nBoards capability is checked against this project, and the\nproject's AI settings take precedence over the workspace's.\n"
                  },
                  "messageText": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 8000,
                    "description": "The focal chat message text to convert into tasks."
                  },
                  "messageId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional source chat message id. Not used today; reserved\nfor future audit linking between the usage event and the\noriginating message.\n"
                  },
                  "contextMessages": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "authorName": {
                          "type": "string",
                          "maxLength": 100,
                          "nullable": true,
                          "description": "Display name of the message author. Optional but improves prompt clarity."
                        },
                        "content": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8000
                        }
                      },
                      "required": [
                        "content"
                      ],
                      "additionalProperties": false
                    },
                    "maxItems": 10,
                    "description": "Preceding chat messages (oldest first) for\nconversational lead-in. The focal message is NOT\nduplicated here.\n"
                  }
                },
                "required": [
                  "workspaceId",
                  "projectId",
                  "messageText"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tasks generated successfully. Credits have been deducted and a usage event recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiTaskGenerationResponse"
                },
                "examples": {
                  "ok": {
                    "summary": "Two tasks generated from the focal message",
                    "value": {
                      "tasks": [
                        {
                          "subject": "Draft launch checklist",
                          "description": "List every step needed before go-live."
                        },
                        {
                          "subject": "Notify stakeholders",
                          "description": "Email the launch plan to leadership."
                        }
                      ],
                      "billedCredits": 4,
                      "balanceAfter": 9996,
                      "eventId": "0193e4a1-b2c3-7000-8000-00000000abcd"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or a gate / tool error. Possible `type` values:\n  - `validationError` — too many `contextMessages`, oversized item, etc.\n  - `aiTaskGenerationInputEmpty` — `messageText` empty/whitespace\n  - `aiTaskGenerationInputTooLong` — `messageText` exceeds the 8000-char cap\n  - `subscriptionNotActive` — the workspace or project has no active subscription\n  - `creditBalanceInsufficient` / `creditOrderExceedsUserDailySpendLimit`\n    — balance below the worst-case estimate, or the caller's daily cap is hit\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. Possible `type` values:\n  - `forbidden` — the caller does not hold `canAiGenerateTasks` on the workspace.\n  - `capabilityNotAvailable` — the project's workspace lacks the\n    `boards` capability, or the workspace lacks the `ai` capability\n    (`errorData.capability` says which).\n  - `aiFeatureNotEnabled` — effective AI settings (project\n    overrides workspace) have `allowAiFeatures` or\n    `aiTaskGenerationEnabled` off (or `aiTaskGenerationAllowReviewer` for reviewers).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failure. Possible `type` values:\n  - `aiProviderFailure` — the AI provider call failed.\n  - `aiProviderEmptyResponse` — the provider returned no usable content.\n  - `aiTaskGenerationInvalidOutput` — the provider returned content\n    that could not be parsed into tasks, or contained zero usable\n    tasks. Safe to retry.\nNo credits are deducted on a provider-side failure; no event is recorded.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/ai/compose": {
      "post": {
        "summary": "One turn of a Compose-with-Nu drafting conversation.",
        "description": "Nu helps the user draft a message for a specific chat. The client sends\nthe drafting conversation so far (`messages`, oldest first); the server\nreads the target chat for context and tone and returns Nu's commentary\n(`text`) plus, when it has one, a `proposal` — the message Nu suggests\nposting. Nothing is posted; the user decides whether the proposal\nreaches their composer.\n\nRequires `canAiComposeMessage` on the workspace, an active\nsubscription, the `ai` capability, `aiComposeEnabled` (and\n`aiComposeAllowReviewer` for reviewers) and sufficient balance — see\nthe AI overview. The caller must also be able to read `chatId`, so\nyou can only compose into a chat you can already read.\n\nCaps: 1–40 turns; each turn's `text` / `proposal` ≤ 4000 characters.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workspace whose credits will be billed and whose AI settings gate the call."
                  },
                  "chatId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The chat being composed into. Read server-side for context; the client never sends transcript content."
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional project context for settings resolution and analytics."
                  },
                  "messages": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "role": {
                          "type": "string",
                          "enum": [
                            "user",
                            "assistant"
                          ]
                        },
                        "text": {
                          "type": "string",
                          "maxLength": 4000,
                          "description": "The turn's text. May be empty."
                        },
                        "proposal": {
                          "type": "string",
                          "maxLength": 4000,
                          "nullable": true,
                          "description": "For assistant turns, the proposal Nu previously offered — replayed so Nu can see what it already suggested."
                        }
                      },
                      "required": [
                        "role",
                        "text"
                      ],
                      "additionalProperties": false
                    },
                    "minItems": 1,
                    "maxItems": 40,
                    "description": "The drafting conversation so far, oldest first."
                  }
                },
                "required": [
                  "workspaceId",
                  "chatId",
                  "messages"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Turn completed. Credits have been deducted and a usage event recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiComposeResponse"
                },
                "examples": {
                  "ok": {
                    "value": {
                      "text": "Here's a short version you could post:",
                      "proposal": "Heads up — the review has moved to Thursday. Same time, same link.",
                      "billedCredits": 4,
                      "balanceAfter": 9993,
                      "eventId": "0193e4a1-b2c3-7000-8000-00000000abcd"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or `subscriptionNotActive`,\n`creditBalanceInsufficient`, `creditOrderExceedsUserDailySpendLimit`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`forbidden` (no `canAiComposeMessage`, or the caller cannot read\n`chatId`), `capabilityNotAvailable`, or `aiFeatureNotEnabled`\n(`aiComposeEnabled` / `aiComposeAllowReviewer` off).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream provider failure (`aiProviderFailure`). No credits are deducted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/ai/feedback": {
      "post": {
        "summary": "Rate one assistant reply (Nu Feedback).",
        "description": "Records a thumbs-up / thumbs-down on a single assistant reply, together\nwith a **snapshot of the interaction that produced it**, for the Nurama\nteam to review.\n\n**What gets stored.** The reply being rated, up to ten preceding turns\nof the conversation, and — when the client supplies them — the page\ncontext and pinned Context-column items that were in play at the time.\nEach captured message is truncated at 4,000 characters. Tell the user\nbefore submitting that context from the interaction will be shared\nwith the Nurama team for review.\n\nThe interaction is **snapshotted, not referenced**: the record captures\nwhat the assistant actually said at the time, and remains useful if\nthe chat is later deleted or the topic rewritten.\n\n**Authorisation.** The caller must be able to read `chatId` — the same\ncheck the chat endpoints apply. A user can only rate a reply in a chat\nthey can already read, so this endpoint cannot be used to capture\nsomeone else's conversation by guessing message ids. Failure is\n`403 forbidden`.\n\nThis endpoint is **not** subject to the subscription, AI-feature or\ncredit-balance checks that gate AI use: feedback can be submitted even\nwhen a workspace's AI add-on has been switched off.\n\nA snapshot that cannot be built does not fail the request — the rating is\nstill recorded, with `context.snapshotError: true`. Losing a thumbs-down\nto a transcript read failure would be the wrong trade.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rating": {
                    "type": "string",
                    "enum": [
                      "positive",
                      "negative"
                    ],
                    "description": "The face the user picked."
                  },
                  "surface": {
                    "type": "string",
                    "enum": [
                      "chat",
                      "assist"
                    ],
                    "description": "Which AI surface produced the reply — `chat` for Nu Chat /\nNu Support, `assist` for Nu @-mentioned inside a regular\nchat. The two run different prompts and tool sets and are\ntuned separately.\n"
                  },
                  "chatId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Chat the rated reply lives in. Required: it is what the\naccess check runs against and what the snapshot is built\nfrom.\n"
                  },
                  "messageId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The assistant reply being rated."
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "The user's own words on what worked or didn't. Optional — a\nrating with no explanation is still a signal.\n"
                  },
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "pageContext": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "The page context the assistant was given for the rated\ninteraction. Loosely typed on purpose — the shape may\nchange over time, and a submission is never rejected over a\nshape drift.\n"
                  },
                  "contextItems": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "description": "Pinned Context-column items, capped at 50."
                  }
                },
                "required": [
                  "rating",
                  "surface",
                  "chatId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Feedback recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                },
                "examples": {
                  "ok": {
                    "value": {
                      "id": "0192f3a4-5b6c-7d8e-9f01-23456789abcf"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — unknown `rating`/`surface`, or notes over 2,000 characters."
          },
          "401": {
            "description": "Unauthorized — missing or invalid bearer token."
          },
          "403": {
            "description": "`forbidden` — the caller cannot read `chatId`. Same gate the chat\nroutes apply, so this also covers a chat that does not exist.\n"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/ai/tones": {
      "get": {
        "summary": "List polish tones available to the chat composer.",
        "description": "Returns the polish tone catalogue so clients can offer tone choices\nwithout hard-coding them. The list is currently fixed; workspace-scoped\ncustom tones are planned.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tone catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tones": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AiTone"
                      }
                    }
                  }
                },
                "examples": {
                  "ok": {
                    "summary": "Default tone catalogue",
                    "value": {
                      "tones": [
                        {
                          "id": "professional",
                          "label": "Professional"
                        },
                        {
                          "id": "casual",
                          "label": "Casual"
                        },
                        {
                          "id": "concise",
                          "label": "Concise"
                        },
                        {
                          "id": "friendly",
                          "label": "Friendly"
                        },
                        {
                          "id": "formal",
                          "label": "Formal"
                        },
                        {
                          "id": "cleanup",
                          "label": "Cleanup"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid bearer token."
          }
        }
      }
    },
    "/ai/balance": {
      "get": {
        "summary": "Get the workspace's current Nurama Credit balance.",
        "description": "Returns the workspace's spendable Nurama Credit balance.\n\nRequires `canGetCreditBalance` on the workspace (workspace owner /\nadmin, project owner / admin) and an active subscription; no\nper-feature gate. The balance is also returned as `balanceAfter` on\nevery billable AI call, so clients can keep a balance display fresh\nwithout a separate fetch after every call.\n\n`balance` is the spendable total: `planBalance` (monthly plan grant,\ndoes not carry over) + `purchasedBalance` (top-ups, accumulates).\n`hasPriorTopUp` reports whether the workspace has ever made a manual\ncredit purchase — the precondition for enabling auto top-up.\n\nAlso available at `GET /credits/balance` (same response).\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace whose Nurama Credit balance to retrieve."
          }
        ],
        "responses": {
          "200": {
            "description": "Current credit balance (integer credits; 0 if the workspace has never been granted credits).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiBalanceResponse"
                },
                "examples": {
                  "ok": {
                    "summary": "Current balance",
                    "value": {
                      "balance": 9997,
                      "planBalance": 5000,
                      "purchasedBalance": 4997,
                      "hasPriorTopUp": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`subscriptionNotActive` — the workspace has no active subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid bearer token."
          },
          "403": {
            "description": "Forbidden — the caller does not hold `canGetCreditBalance` on the workspace.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/ai/usage": {
      "get": {
        "summary": "Aggregated Nurama Credit usage report for a workspace.",
        "description": "Credit spend on AI features (and convos) in a time window, totalled\nand broken down by user and by integration point. Defaults to a\nrolling 30-day window ending now when no dates are passed. Filters\napply uniformly: `userId` narrows the per-integration breakdown to\nthat user's calls, and `integrationPoint` narrows the per-user\nbreakdown to that feature.\n\nRequires `canGetCreditUsageReport` on the workspace (granted to the\nsame roles as `canGetCreditBalance`) and an active subscription.\n\nAlso available at `GET /credits/usage` (same response).\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "ISO 8601 date-time. Defaults to 30 days before `endDate`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "ISO 8601 date-time; must not be before `startDate`. Defaults to now."
          },
          {
            "name": "userId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "integrationPoint",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "polish",
                "chat",
                "taskGeneration",
                "imageRevision",
                "convo"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Usage report for the window.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiUsageReportResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (bad dates, unknown `integrationPoint`), or `subscriptionNotActive`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the caller does not hold `canGetCreditUsageReport` on the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/ai/chat/topics": {
      "get": {
        "summary": "List the caller's AI chat topics for a scope.",
        "description": "Returns topics owned by the calling user, scoped to one\nworkspace / project, or to \"social\" (personal) chats. Topics are\nordered by `lastMessageAt` desc with nulls last.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "scopeType",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "workspace",
                "project",
                "social"
              ]
            }
          },
          {
            "name": "scopeId",
            "in": "query",
            "required": false,
            "schema": {},
            "description": "Required when `scopeType` is `workspace` or `project`; must be omitted for `social`."
          },
          {
            "name": "archived",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Topic list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiChatTopicListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or `subscriptionNotActive`."
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden — the scope-access check failed (no `canGetProject` /\n`canGetWorkspace` on `scopeId`; `forbidden`), the workspace lacks\nthe `ai` capability (`capabilityNotAvailable`), or `aiChatEnabled`\nresolved false (`aiFeatureNotEnabled`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      },
      "post": {
        "summary": "Create a new (empty) AI chat topic.",
        "description": "Creates a new, empty topic. Send the first message through\n`POST /v1/chats/{topic.chatId}/new-message`; the assistant's reply\narrives asynchronously as an `aiChatMessageCreate` notification (see\nthe AI overview). The balance check runs when a message is sent, so\nno credits are charged by this endpoint.\n\nThe first user/assistant exchange triggers an auto-naming pass; the\nresulting title arrives via the `aiChatTopicUpdate` notification.\n\nRequires access to the scope (`canGetProject` / `canGetWorkspace` on\n`scopeId`; no check for `social`), an active subscription, the `ai`\ncapability and `aiChatEnabled`.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workspace whose credits will be billed for the conversation."
                  },
                  "scopeType": {
                    "type": "string",
                    "enum": [
                      "workspace",
                      "project",
                      "social"
                    ]
                  },
                  "scopeId": {
                    "description": "Required when scopeType is `workspace` or `project`; must be omitted for `social`."
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Optional user-supplied title; otherwise the topic is auto-named after the first exchange."
                  }
                },
                "required": [
                  "workspaceId",
                  "scopeType"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Topic created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiChatCreateTopicResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (bad scope shape, `scopeId` present on a social topic, etc.), or `subscriptionNotActive`."
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden — scope access (`forbidden`), `ai` capability\n(`capabilityNotAvailable`), or `aiChatEnabled` (`aiFeatureNotEnabled`) gate failed.\n"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/ai/chat/topics/{topicId}": {
      "get": {
        "summary": "Get a single AI chat topic.",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "topicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "workspaceId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The topic.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topic": {
                      "$ref": "#/components/schemas/AiChatTopic"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`subscriptionNotActive` — `workspaceId` has no active subscription."
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden — the caller is not the topic's creator (`forbidden`),\nthe workspace lacks the `ai` capability (`capabilityNotAvailable`),\nor `aiChatEnabled` is off (`aiFeatureNotEnabled`).\n"
          },
          "404": {
            "description": "Topic not found. `type` is `aiChatTopicNotFound`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      },
      "patch": {
        "summary": "Rename, archive, or set the context pins of an AI chat topic.",
        "description": "At least one of `title`, `archived`, or `contextItems` must be\nprovided. Only the topic's creator may update it; any other caller\nreceives `forbidden`.\n\n## Context pins (`contextItems`)\n\nThe items the user has pinned to Nu's Context column for this topic —\nassets, folders, people, submissions, and public collections that the\nconversation is \"about\". Persisted here rather than sent per message,\nso the column survives a page reload and a new session the way the\nconversation does.\n\nSent and stored as **identity only** (`type` + `id`); the server\nnormalises away any other field. Names, thumbnails, and content are\nnever stored, and neither is any access decision.\n\nA pin is a **bookmark, not a grant**. Every entry is re-resolved from\nscratch on every turn, under the caller's live permissions:\n\n  - an item deleted since it was pinned drops out of that turn;\n  - an item the caller has since lost access to drops out;\n  - visibility tiers are re-derived per turn — the stored pin\n    carries none.\n\nDropped entries are silently omitted from the model's context rather\nthan reported, so the assistant never learns that something it cannot\nsee exists. The stored list is left intact, so regaining access\nrestores the pin without re-pinning.\n\nSending `contextItems` replaces the topic's pins wholesale. An empty\narray clears them. Only project-scoped topics resolve pins at all;\nworkspace- and social-scoped topics have no project to resolve\nagainst and ignore them.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "topicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "archived": {
                    "type": "boolean"
                  },
                  "contextItems": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "asset",
                            "folder",
                            "user",
                            "submission",
                            "public"
                          ]
                        },
                        "id": {
                          "type": "string",
                          "maxLength": 200,
                          "description": "UUID for every type except `public`, which is keyed by its share token.\n"
                        }
                      },
                      "required": [
                        "type",
                        "id"
                      ],
                      "additionalProperties": false
                    },
                    "maxItems": 20,
                    "description": "Replaces the topic's Context-column pins wholesale. An empty array clears them. Identity only — see the endpoint description for how these are re-resolved per turn.\n"
                  }
                },
                "required": [
                  "workspaceId"
                ],
                "additionalProperties": false,
                "x-dependencies": [
                  {
                    "or": [
                      "title",
                      "archived",
                      "contextItems"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated topic.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topic": {
                      "$ref": "#/components/schemas/AiChatTopic"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (none of `title` / `archived` / `contextItems` supplied), or `subscriptionNotActive`."
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden — not the topic's creator, `capabilityNotAvailable`, or `aiFeatureNotEnabled`."
          },
          "404": {
            "description": "Topic not found (`aiChatTopicNotFound`)."
          }
        },
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete an AI chat topic.",
        "description": "Soft-deletes the topic: the topic and every message in its chat move\nto `status: pendingDelete`, and an `aiChatTopicUpdate` notification\nwith a `delete` change is emitted on the caller's user channel. Only\nthe topic's creator may delete it.\n",
        "tags": [
          "AI"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "topicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "workspaceId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Topic deleted. Returns the topic with `status: pendingDelete`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topic": {
                      "$ref": "#/components/schemas/AiChatTopic"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`subscriptionNotActive` — `workspaceId` has no active subscription."
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden — not the topic's creator, `capabilityNotAvailable`, or `aiFeatureNotEnabled`."
          },
          "404": {
            "description": "Topic not found (`aiChatTopicNotFound`)."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/ai/revisions": {
      "post": {
        "tags": [
          "AI"
        ],
        "summary": "Generate an AI image revision.",
        "description": "Generate a variation of an existing image (or a captured video\nframe) guided by a free-text prompt. The result is staged as a\ntemporary Scratch upload — call `POST /scratch/{id}/promote` to turn\nthe chosen revision into a real asset on the destination project\n(there is no `/ai/revisions/promote` route).\n\n`source` must contain exactly one of `url` (public media URL),\n`scratchId` (an upload created via `POST /ai/revisions/source-upload`\n— the preferred path for video frames; it must belong to the caller\nand workspace and still be active), or `dataUrl` (deprecated legacy\npath for video frames).\n\nRequires `canAiGenerateImageRevision` on the workspace, an active\nsubscription, the `ai` capability, the `aiImageRevisionEnabled`\nworkspace/project setting (`aiImageRevisionAllowReviewer` for\nreviewers) and a worst-case credit pre-check (a flat estimate).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "assetId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional, audit-only: the asset whose public media URL\nwas supplied as `source.url`. Recorded for lineage.\n"
                  },
                  "source": {
                    "type": "object",
                    "properties": {
                      "url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Public media URL of the source image."
                      },
                      "dataUrl": {
                        "type": "string",
                        "pattern": "^data:image\\/[a-zA-Z0-9.+-]+;base64,",
                        "description": "Base64 image data URL. Legacy path — prefer `scratchId`.",
                        "deprecated": true
                      },
                      "scratchId": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Scratch upload id returned by `POST /ai/revisions/source-upload`."
                      }
                    },
                    "additionalProperties": false,
                    "x-dependencies": [
                      {
                        "xor": [
                          "url",
                          "dataUrl",
                          "scratchId"
                        ]
                      }
                    ],
                    "description": "Exactly one of `url`, `scratchId` or `dataUrl` (sending\ntwo is a validation error).\n"
                  },
                  "prompt": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1000,
                    "description": "Plain-language description of the desired edit."
                  },
                  "references": {
                    "type": "array",
                    "items": {
                      "oneOf": [
                        {
                          "type": "string",
                          "format": "uri"
                        },
                        {
                          "type": "string",
                          "pattern": "^data:image\\/[a-zA-Z0-9.+-]+;base64,"
                        }
                      ]
                    },
                    "maxItems": 8,
                    "description": "Optional reference images (public URLs or base64 image\ndata URLs). Used for multi-image conditioning when\niterating on a prior revision.\n"
                  },
                  "mask": {
                    "type": "object",
                    "properties": {
                      "dataUrl": {
                        "type": "string",
                        "pattern": "^data:image\\/png;base64,",
                        "description": "`data:image/png;base64,…` — PNG only (JPEG has no alpha channel)."
                      }
                    },
                    "required": [
                      "dataUrl"
                    ],
                    "additionalProperties": false,
                    "description": "Optional alpha-channel mask. PNG data URL matching\nthe source image's dimensions. Transparent pixels\nmark the region to edit; opaque pixels are preserved.\nApplied as a true mask, not as another `references[]`\nentry. When supplied, the prompt is automatically\naugmented with a region directive.\n"
                  },
                  "jobId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Groups all revisions made in one editing session, for cleanup and analytics."
                  },
                  "sourceAspect": {
                    "type": "number",
                    "minimum": 0.1,
                    "x-exclusiveMinimum": true,
                    "maximum": 10,
                    "description": "Source image aspect ratio (width / height). Drives the\ngeneration size bucket (square / landscape / portrait) and\nthe post-generate pad-to-aspect step.\n"
                  }
                },
                "required": [
                  "workspaceId",
                  "source",
                  "prompt"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generation succeeded; returns the scratch id + public URL.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revisionId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "revisionUrl": {
                      "type": "string",
                      "nullable": true
                    },
                    "model": {
                      "type": "string"
                    },
                    "quality": {
                      "type": "string"
                    },
                    "billedCredits": {
                      "type": "integer"
                    },
                    "balanceAfter": {
                      "type": "integer",
                      "nullable": true
                    },
                    "eventId": {
                      "type": "string",
                      "format": "uuid",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. `aiRevisionMaskInvalid` when `mask.dataUrl`\nisn't a PNG data URL. `aiRevisionPromptEmpty` /\n`aiRevisionPromptTooLong` on prompt-shape errors.\n`aiRevisionTooManyReferences` when `references[]` exceeds\nthe maximum (8). `aiRevisionSourceMissing` when the source is\nabsent or `scratchId` does not resolve to an active\nrevision-source upload. Also `subscriptionNotActive`,\n`creditBalanceInsufficient`, `creditOrderExceedsUserDailySpendLimit`.\n"
          },
          "401": {
            "description": "Unauthenticated."
          },
          "403": {
            "description": "Forbidden — `forbidden` (no `canAiGenerateImageRevision`, or the\n`scratchId` belongs to another user / workspace),\n`capabilityNotAvailable`, or `aiFeatureNotEnabled` when\n`aiImageRevisionEnabled` is off.\n"
          },
          "502": {
            "description": "Upstream provider failure (`aiProviderFailure`)."
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/ai/revisions/source-upload": {
      "post": {
        "tags": [
          "AI"
        ],
        "summary": "Mint a Scratch upload bundle for an image-revision source frame.",
        "description": "Returns signed multipart upload URLs the client uses to upload a\nsource image (a captured video frame — always JPEG) directly to\nstorage BEFORE calling `POST /ai/revisions` with `source.scratchId`.\nThe bytes never pass through the API. After uploading the parts,\ncomplete the upload via `POST /v1/scratch/{scratchId}/complete-upload`.\n\nSame checks as `POST /ai/revisions` minus the balance check (staging\nis free): `canAiGenerateImageRevision`, an active subscription, the\n`ai` capability and `aiImageRevisionEnabled`.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "assetId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional, audit-only lineage — the asset the frame was captured from."
                  },
                  "sizeInMB": {
                    "type": "number",
                    "minimum": 0,
                    "x-exclusiveMinimum": true,
                    "maximum": 25,
                    "description": "Size of the JPEG in MB; decides how many upload parts are issued. Maximum 25 MB (the provider's per-image cap)."
                  }
                },
                "required": [
                  "workspaceId",
                  "sizeInMB"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload bundle. Multipart-upload the bytes to `urls`, then complete the upload.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scratchId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Pass as `source.scratchId` to `POST /ai/revisions`."
                    },
                    "key": {
                      "type": "string",
                      "description": "Storage key of the uploaded object."
                    },
                    "uploadId": {
                      "type": "string"
                    },
                    "urls": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uri"
                      },
                      "description": "Per-part signed PUT URLs."
                    },
                    "expires": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the staged upload expires."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or `subscriptionNotActive`."
          },
          "401": {
            "description": "Unauthenticated."
          },
          "403": {
            "description": "`forbidden`, `capabilityNotAvailable`, or `aiFeatureNotEnabled` (`aiImageRevisionEnabled` off)."
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}": {
      "get": {
        "summary": "Get asset information with optional chat data",
        "description": "Retrieve information for a specific asset by ID. Optionally include chat data by specifying chatVisibility.",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to retrieve."
          },
          {
            "name": "chatVisibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Visibility level of chat to include in response. If not specified, no chat data will be populated."
          },
          {
            "name": "chatMessageSort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for chat messages."
          },
          {
            "name": "chatReplySort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for chat replies."
          },
          {
            "name": "chatMessageLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of chat messages to return."
          },
          {
            "name": "chatReplyLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of chat replies to return per message."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved asset information.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Asset"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "chats": {
                          "type": "object",
                          "description": "Chat data for the asset (only present if chatVisibility is specified)",
                          "properties": {
                            "creator": {
                              "allOf": [
                                {
                                  "$ref": "#/components/schemas/Chat"
                                }
                              ],
                              "description": "Creator chat data (only present if chatVisibility includes 'creator')"
                            },
                            "reviewer": {
                              "allOf": [
                                {
                                  "$ref": "#/components/schemas/Chat"
                                }
                              ],
                              "description": "Reviewer chat data (only present if chatVisibility includes 'reviewer')"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request - Invalid asset ID or query parameters."
          },
          "401": {
            "description": "Unauthorized - Authentication required."
          },
          "403": {
            "description": "Forbidden - User does not have permission to access the asset or specified chat visibility level."
          },
          "404": {
            "description": "Asset not found."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update asset",
        "description": "Modify an existing asset (name, meta, sizeInBytes, status). Requires `canUpdateAsset`.\n`status` cannot be set to `pendingDelete` here — deletion goes through `DELETE /assets/{assetId}`.\n",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to update."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "New display name. HTML is sanitised.",
                    "example": "Asset Name"
                  },
                  "meta": {
                    "type": "object"
                  },
                  "sizeInBytes": {
                    "type": "number",
                    "minimum": 0
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "pendingUpload",
                      "pendingPostProcessing",
                      "postProcessingError",
                      "inactive"
                    ],
                    "description": "Any asset status except `pendingDelete`."
                  }
                },
                "additionalProperties": false,
                "example": {
                  "name": "New Asset Name",
                  "meta": {
                    "key1": "value1",
                    "key2": "value2"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated asset information.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "400": {
            "description": "Bad request."
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden - the user does not have permission to update the asset."
          },
          "404": {
            "description": "Asset not found."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Mark asset for deletion",
        "description": "Mark an asset as deleted.",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to mark for deletion."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully marked asset for deletion. Returns the asset with `status` set to `pendingDelete`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "400": {
            "description": "Bad request."
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden - the user does not have permission to delete the asset."
          },
          "404": {
            "description": "Asset not found."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/references": {
      "get": {
        "summary": "List every location an asset is referenced",
        "description": "One asset can be referenced from many places at once — its own folder, the reviewer tree, any submission it was added to, any public release it was shared in. They all point at the same asset, which is why renaming it retitles it everywhere, and why deleting the last reference in its primary file system removes the rest.\n\nPRIMARY references are the asset's home file system; SECONDARY are everything else, grouped by collection. Only collections with at least one reference appear in `counts`.\n",
        "tags": [
          "Assets"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the asset"
          }
        ],
        "responses": {
          "200": {
            "description": "Every location the asset is referenced",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetReferences"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/file/{fileId}": {
      "get": {
        "summary": "Get file from asset",
        "description": "Retrieve a specific file from an asset by asset ID and file ID. Requires `canGetAsset`.\nPrivate fields (`bucketName`, `tags`, `postProcessingTasks`) are stripped from the response.\n",
        "tags": [
          "Assets"
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved file information.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/FileNotFound"
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden - the user does not have permission to get the file."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset containing the file."
          },
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the file to retrieve."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/function-type/{functionType}": {
      "get": {
        "summary": "Get files by function type",
        "description": "Retrieve all files from an asset filtered by function type. Requires `canGetAsset`.\nPrivate fields (`bucketName`, `tags`, `postProcessingTasks`) are stripped from each file.\n",
        "tags": [
          "Assets"
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved files of the specified function type.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/File"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/FileNotFound"
          },
          "401": {
            "description": "Unauthorized."
          },
          "403": {
            "description": "Forbidden - the user does not have permission to get the files."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to retrieve files from."
          },
          {
            "name": "functionType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "original",
                "thumbnail",
                "stream",
                "media",
                "customThumbnailOriginal",
                "customThumbnail",
                "documentText"
              ]
            },
            "description": "The function type of files to filter by."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/repair": {
      "post": {
        "summary": "Bulk regenerate signed upload links for assets in status pendingUpload.",
        "description": "Marks every existing file on each asset `pendingDelete` and mints a fresh multipart\nupload URL set so the client can re-upload the original. Requires `canRepairAssets`.\nPer-asset failures are reported inline (`status: 'error'`) rather than failing the request.\n",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "assetIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 100
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result entry per requested asset.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RepairAssetAndSignedLinks"
                }
              }
            }
          },
          "400": {
            "description": "Bad request."
          },
          "403": {
            "description": "Forbidden - the user does not have permission to repair one or more assets."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/assets/complete-upload": {
      "post": {
        "summary": "Complete multipart upload",
        "description": "Complete a multipart upload for an asset file.\n\nThis endpoint finalizes the multipart upload by combining all uploaded parts,\nregisters the original file on the asset (`assetId` is required) and triggers\nasset completion and media post-processing.\n\n**Two-step workflow:**\n1. Upload the file parts to the signed URLs returned when the asset was created\n2. Call this endpoint with `assetId` to complete the upload and trigger processing\n\nPost-processing failures after the upload has been committed do not fail the\nrequest — they are reported via the optional `warning` field.\n",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Storage key of the upload, as returned with the signed upload URLs."
                  },
                  "uploadId": {
                    "type": "string",
                    "description": "Multipart upload ID returned with the signed upload URLs."
                  },
                  "parts": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "ETag": {
                          "type": "string"
                        },
                        "PartNumber": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "ETag",
                        "PartNumber"
                      ],
                      "additionalProperties": false
                    },
                    "description": "Array of uploaded parts with ETags"
                  },
                  "assetId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the asset the upload belongs to. Once the upload is committed the original\nfile is registered on the asset and asset completion / media processing is triggered.\n"
                  }
                },
                "required": [
                  "key",
                  "uploadId",
                  "assetId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload committed to storage. `warning` is present when the asset could not be found or post-processing could not be triggered.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "completed"
                      ]
                    },
                    "warning": {
                      "type": "string",
                      "description": "Present only when a non-fatal follow-up step failed.",
                      "example": "Post-processing failed"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/assets/page/{assetId}": {
      "get": {
        "summary": "Get the page an asset would appear on in paginated results.",
        "description": "Returns the 1-based page number the asset would appear on when listing assets of the\nsame owner resource and media type with the given sort and page size. Requires `canGetAsset`.\nThe asset must be `active`.\n",
        "tags": [
          "Assets"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to find the page number for"
          },
          {
            "name": "visibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 20,
              "default": 20
            },
            "description": "Page size used to compute the page number."
          },
          {
            "name": "inFolder",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "When true, the page is computed within the asset's folder rather than the whole owner resource."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved asset page data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer",
                      "example": 3
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AssetNotFound"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/tag": {
      "put": {
        "summary": "Add tag to asset",
        "description": "Add a tag to an asset.",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the tag to add to the asset"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag added to asset successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/AssetNotFound"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to tag."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/untag": {
      "put": {
        "summary": "Remove tag from asset",
        "description": "Remove a tag from an asset.",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the tag to remove from the asset"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag removed from asset successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/AssetNotFound"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to untag."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/download": {
      "post": {
        "summary": "Download assets",
        "description": "Generate download links for the specified assets/files. Requires `canDownloadAssets`.\nSide effect: a `download` event is recorded in each asset's access activity\nwith the `visibility` given in the request body (defaults to `creator`).\nRecording the activity never affects the download response.\n\nNote: the request body is currently accepted without validation; the limits\nbelow reflect the intended contract.\n",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "assetIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 100
                  },
                  "visibility": {
                    "type": "string",
                    "enum": [
                      "creator",
                      "reviewer"
                    ],
                    "description": "Audience side the download was initiated from; recorded as the\n`visibility` of the access-activity event.\nDefaults to `creator` when omitted.\n"
                  }
                },
                "required": [
                  "assetIds"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully generated download links.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DownloadSignedUrlData"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetIds",
            "in": "path",
            "required": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              },
              "maxItems": 100
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/custom-thumbnail/upload-url": {
      "post": {
        "summary": "Mint a signed multipart upload URL for a custom thumbnail",
        "description": "Returns a multipart upload URL set for a user-supplied custom thumbnail\nimage. Once the upload is finalized via\n`POST /assets/{assetId}/custom-thumbnail/complete-upload`, background\nprocessing generates the sized `customThumbnail` files and registers\nthem on the asset.\n\nSide effect: any prior active `customThumbnailOriginal` / `customThumbnail`\nfiles on the asset are set to `inactive` before the URL is issued so the\nnew upload replaces them cleanly. If the upload is abandoned, the asset's\nauto-generated `thumbnail` files remain and should be displayed instead.\n\nCustom thumbnails are only supported for `video` and `audio` assets.\n",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fileName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Original file name; only its extension is used for the stored file.",
                    "example": "cover.jpg"
                  },
                  "mimeType": {
                    "type": "string",
                    "enum": [
                      "image/jpeg",
                      "image/png",
                      "image/gif",
                      "image/tiff",
                      "image/bmp",
                      "image/webp",
                      "image/svg+xml",
                      "image/heif",
                      "image/vnd.microsoft.icon",
                      "image/aces",
                      "image/heic"
                    ],
                    "description": "MIME type of the upload. Must correspond to one of the platform's\nsupported image types (see `supportedFileTypes.image` in `GET /config`).\n",
                    "example": "image/jpeg"
                  },
                  "sizeInMB": {
                    "type": "number",
                    "minimum": 0,
                    "x-exclusiveMinimum": true,
                    "maximum": 10,
                    "description": "Approximate file size in megabytes; must be positive, capped at 10.",
                    "example": 1.2
                  }
                },
                "required": [
                  "fileName",
                  "mimeType",
                  "sizeInMB"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Multipart upload URL set returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fileName": {
                      "type": "string",
                      "description": "File name the upload is stored under (`<assetId>.<ext>`)."
                    },
                    "assetId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "uploadId": {
                      "type": "string",
                      "description": "Multipart upload ID; pass it back to `/complete-upload`."
                    },
                    "key": {
                      "type": "string",
                      "description": "Storage key the upload writes to; pass it back to `/complete-upload`."
                    },
                    "urls": {
                      "type": "array",
                      "description": "Signed PUT URLs, one per part.",
                      "items": {
                        "type": "string"
                      }
                    },
                    "mimeType": {
                      "type": "string"
                    },
                    "expires": {
                      "type": "integer",
                      "description": "Unix timestamp when the signed URLs expire."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid mediaType (only video/audio supported), invalid mimeType, or invalid request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks `canUpdateAsset` on this asset.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the video or audio asset to attach a custom thumbnail to."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/custom-thumbnail/complete-upload": {
      "post": {
        "summary": "Finalize the multipart upload of a custom thumbnail",
        "description": "Commits the multipart upload of a custom thumbnail. Background processing\nthen generates the sized `customThumbnail` files and registers them on the\nasset. Unlike `/v1/assets/complete-upload`, this does not register an\noriginal file on the asset and does not trigger media transcoding.\n",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Storage key of the in-progress multipart upload, as returned from `/upload-url`."
                  },
                  "uploadId": {
                    "type": "string",
                    "description": "Multipart upload ID returned from `/upload-url`."
                  },
                  "parts": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "ETag": {
                          "type": "string"
                        },
                        "PartNumber": {
                          "type": "integer",
                          "minimum": 1
                        }
                      },
                      "required": [
                        "ETag",
                        "PartNumber"
                      ],
                      "additionalProperties": false
                    },
                    "minItems": 1,
                    "description": "ETags collected from each part PUT, in order."
                  }
                },
                "required": [
                  "key",
                  "uploadId",
                  "parts"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload finalized.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "completed"
                      ]
                    },
                    "key": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks `canUpdateAsset` on this asset.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/custom-thumbnail": {
      "delete": {
        "summary": "Remove the custom thumbnail from an asset",
        "description": "Soft-deletes every active `customThumbnailOriginal` and `customThumbnail`\nfile on the asset by setting their status to `inactive`, decrementing the\nasset's `sizeInBytes` and storage quota by the bytes that were active.\nClients should fall back to the auto-generated `thumbnail` files.\nBroadcasts an `assetFileUpdate` WebSocket event so connected clients can\nre-render without a refetch.\n",
        "tags": [
          "Assets"
        ],
        "responses": {
          "200": {
            "description": "Custom thumbnail removed; returns the updated asset.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden — caller lacks `canUpdateAsset` on this asset.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/document-url": {
      "get": {
        "summary": "Get a signed inline-view URL for a document asset",
        "description": "Returns a short-lived signed URL for rendering a `document` asset inline in the\nviewer. Serves the processed `media` derivative (an optimised PDF with permission\nflags stripped), not the original — downloads of the original go through\n`POST /assets/download`. Requires `canGetAsset`.\n\nThe asset must be `active`, have `mediaType: 'document'`, and have an active\n`media` file (i.e. post-processing finished successfully); otherwise a 400 is\nreturned and the document cannot be viewed inline.\n\nThe `media` PDF is capped at a maximum page count, so `pagesTruncated`,\n`truncatedFrom` and `originalPageCount` tell the viewer whether and where pages\nare missing.\n",
        "tags": [
          "Assets"
        ],
        "responses": {
          "200": {
            "description": "Signed view URL and document page metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "Signed GET URL (`Content-Type: application/pdf`, `Content-Disposition: inline`)."
                    },
                    "expires": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Unix timestamp (seconds) when the signed URL expires.",
                      "example": 1705988395
                    },
                    "pageCount": {
                      "type": "integer",
                      "description": "Number of pages in the served (possibly truncated) PDF."
                    },
                    "pagesTruncated": {
                      "type": "boolean",
                      "description": "True when the served PDF holds only part of the original document."
                    },
                    "truncatedFrom": {
                      "type": "string",
                      "enum": [
                        "start",
                        "end"
                      ],
                      "description": "Which end of the document the served pages were kept from."
                    },
                    "originalPageCount": {
                      "type": "integer",
                      "description": "Page count of the original document."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`assetNotFound` (asset missing or not active), `invalidAssetType` (not a document),\nor `fileNotFound` (no active `media` file yet).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the document asset to view."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/promote-to-project": {
      "post": {
        "summary": "Promote a chat-message attachment to a project asset",
        "description": "Copies a chat-message attachment (an asset with `ownerResourceType: 'chat'` and\n`functionType: 'attachment'`) into the destination project as a new, fully\nindependent project asset. Deleting either side afterwards has no effect on the other.\n\nPermission is `canCreateAsset` on the destination project (`body.projectId`), which\nexcludes reviewers. The source chat must live in the same workspace as the\ndestination project.\n\nIdempotent: promoting the same source into the same project again returns the\nexisting promoted asset with `deduped: true`. When post-processing is required the\nnew asset is returned before it is `active`; the usual asset activation notifications\nfire when processing completes.\n",
        "tags": [
          "Assets"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Destination project."
                  },
                  "fileName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Display name for the new project asset. Defaults to the source attachment's name."
                  }
                },
                "required": [
                  "projectId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The promoted project asset.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asset": {
                      "$ref": "#/components/schemas/Asset"
                    },
                    "deduped": {
                      "type": "boolean",
                      "description": "True when an earlier promote of the same source into the same project was returned instead of creating a new asset."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`assetNotFound` (source missing), `attachmentNotPromotable` (source is not an\nactive chat-message attachment), or a validation error.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Caller lacks `canCreateAsset` on the destination project, or\n`attachmentPromotionWorkspaceMismatch` (source chat and destination project are\nin different workspaces).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          },
          "500": {
            "description": "`attachmentPromotionFailed` — the copy failed mid-way; the partial asset is marked for deletion.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the chat-message attachment asset to promote."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/boards": {
      "post": {
        "summary": "Create a new board",
        "description": "Creates a new kanban board for a project with default or custom columns. Default columns are Backlog, To Do, In Progress, In Review, and Done.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The project this board belongs to"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Board name"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "nullable": true,
                    "description": "Board description"
                  },
                  "visibility": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "creator",
                        "reviewer"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 2,
                    "default": [
                      "creator"
                    ],
                    "description": "Who can see this board. Must include `creator` (`['reviewer']` alone is rejected)."
                  },
                  "columns": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 50
                        },
                        "description": {
                          "type": "string",
                          "maxLength": 500,
                          "nullable": true
                        },
                        "color": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "#6B7280",
                            "#3B82F6",
                            "#F59E0B",
                            "#8B5CF6",
                            "#10B981",
                            "#EF4444",
                            "#F97316",
                            "#EC4899",
                            "#14B8A6",
                            "#6366F1",
                            "#84CC16",
                            "#06B6D4"
                          ],
                          "description": "Hex color from approved column colors"
                        },
                        "isDefault": {
                          "type": "boolean",
                          "default": false,
                          "description": "Whether new tasks are placed here by default"
                        },
                        "taskStatus": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "pending",
                            "inProgress",
                            "complete",
                            "closed"
                          ],
                          "description": "When set, tasks moved into this column have their status set to this value"
                        },
                        "sortOrder": {
                          "type": "number"
                        },
                        "reviewersCanContribute": {
                          "type": "boolean",
                          "description": "When true, reviewer-role users can create tasks in this column on a reviewer-visibility board"
                        }
                      },
                      "required": [
                        "name"
                      ],
                      "additionalProperties": false
                    },
                    "description": "Custom columns (omit for defaults)"
                  }
                },
                "required": [
                  "projectId",
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Board created successfully with columns",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Board"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/boards/project/{projectId}": {
      "get": {
        "summary": "Get boards for a project",
        "description": "Returns all active boards for a project, filtered by the user's visibility role. Each board includes its columns. Optional search and tags filters.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The project to get boards for"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Case-insensitive substring match on board name"
          },
          {
            "name": "visibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Narrow results to a specific visibility (must be a subset of what the caller can already access)"
          },
          {
            "name": "tags",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              },
              "x-accepts-single-value": true
            },
            "style": "form",
            "explode": true,
            "description": "Filter by one or more tag IDs (matches boards containing any of the supplied tags)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of boards with columns",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Board"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}": {
      "get": {
        "summary": "Get a board with columns and tasks",
        "description": "Returns a board with all its columns and tasks grouped by column. Tasks are populated with creator, assignedTo, and project.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Board with columns and grouped tasks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoardWithTasks"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the board"
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update a board",
        "description": "Update board name, description, visibility, status, or sort order. Requires\n`canUpdate{Creator|Reviewer}Board` for the board's visibility.\n\nNarrowing `visibility` when tasks, assignees or task relations depend on the\nremoved tier returns 409 `boardVisibilityChangeBlocked`. Re-issue the request with\n`cascade: true` to apply the cleanup automatically (re-stamp tasks, unassign blocked\nassignees, delete blocked relations); the applied cleanup is reported in the\nresponse's `cascade` field.\n",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "nullable": true
                  },
                  "visibility": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "creator",
                        "reviewer"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Must include `creator`."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "archived"
                    ]
                  },
                  "sortOrder": {
                    "type": "number"
                  },
                  "cascade": {
                    "type": "boolean",
                    "description": "Opt in to automatic cleanup when narrowing visibility would otherwise be blocked."
                  }
                },
                "additionalProperties": false,
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Board updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Board"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "cascade": {
                          "type": "object",
                          "nullable": true,
                          "description": "Cleanup applied because `cascade: true` was passed; `null` when no cascade ran.",
                          "properties": {
                            "restampedTaskIds": {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "format": "uuid"
                              }
                            },
                            "unassignedTasks": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "taskId": {
                                    "type": "string",
                                    "format": "uuid"
                                  },
                                  "assignedToId": {
                                    "type": "string",
                                    "format": "uuid"
                                  }
                                }
                              }
                            },
                            "removedRelationIds": {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "format": "uuid"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Board not found (`boardNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`boardVisibilityChangeBlocked` — removing the visibility would orphan tasks, assignees or related chats; retry with `cascade: true`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the board"
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a board",
        "description": "Deletes a board and its columns. The optional body controls what happens\nto tasks on the board:\n\n  - `unassign` (default) — set `boardId` and `columnId` to null on each task. Tasks are kept.\n  - `delete` — delete every task on the board, plus its TaskRelation, TaskLink, and TaskEvent rows.\n  - `reassign` — move tasks to `targetBoardId` / `targetColumnId` (defaults to the target's default column). If the target column has a `taskStatus`, the task's `status` is updated to match.\n\nFor `reassign`, the caller must also have Update access on the target board's tasks\n(`targetBoardRequired` when `targetBoardId` is missing, `targetBoardSameAsSource`\nwhen it equals the board being deleted). Requires `canDelete{Creator|Reviewer}Board`.\n",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "disposition": {
                    "type": "string",
                    "enum": [
                      "unassign",
                      "delete",
                      "reassign"
                    ]
                  },
                  "targetBoardId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Required when disposition is 'reassign'."
                  },
                  "targetColumnId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional. Defaults to the target board's default column."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Board deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "disposition": {
                      "type": "string",
                      "enum": [
                        "unassign",
                        "delete",
                        "reassign"
                      ]
                    },
                    "deletedTaskIds": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uuid"
                      }
                    },
                    "reassignedTaskIds": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the board"
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/columns": {
      "post": {
        "summary": "Add a column to a board",
        "description": "Adds a new column to the board. If sortOrder is not provided, it is placed after the last column.",
        "tags": [
          "Board Columns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 50
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "nullable": true
                  },
                  "color": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "#6B7280",
                      "#3B82F6",
                      "#F59E0B",
                      "#8B5CF6",
                      "#10B981",
                      "#EF4444",
                      "#F97316",
                      "#EC4899",
                      "#14B8A6",
                      "#6366F1",
                      "#84CC16",
                      "#06B6D4"
                    ]
                  },
                  "isDefault": {
                    "type": "boolean",
                    "default": false
                  },
                  "taskStatus": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "pending",
                      "inProgress",
                      "complete",
                      "closed"
                    ],
                    "description": "When set, tasks moved into this column have their status set to this value"
                  },
                  "sortOrder": {
                    "type": "number"
                  },
                  "reviewersCanContribute": {
                    "type": "boolean",
                    "description": "When true, reviewer-role users can create tasks in this column on a reviewer-visibility board"
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Column created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoardColumn"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/columns/reorder": {
      "put": {
        "summary": "Reorder columns",
        "description": "Updates the sortOrder of multiple columns at once.",
        "tags": [
          "Board Columns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "columns": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "sortOrder": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "id",
                        "sortOrder"
                      ],
                      "additionalProperties": false
                    },
                    "minItems": 1
                  }
                },
                "required": [
                  "columns"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Columns reordered successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BoardColumn"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/columns/{columnId}": {
      "put": {
        "summary": "Update a column",
        "description": "Update column name, description, color, isDefault, taskStatus, sortOrder, or reviewersCanContribute.",
        "tags": [
          "Board Columns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 50
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "nullable": true
                  },
                  "color": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "#6B7280",
                      "#3B82F6",
                      "#F59E0B",
                      "#8B5CF6",
                      "#10B981",
                      "#EF4444",
                      "#F97316",
                      "#EC4899",
                      "#14B8A6",
                      "#6366F1",
                      "#84CC16",
                      "#06B6D4"
                    ]
                  },
                  "isDefault": {
                    "type": "boolean"
                  },
                  "taskStatus": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "pending",
                      "inProgress",
                      "complete",
                      "closed"
                    ]
                  },
                  "sortOrder": {
                    "type": "number"
                  },
                  "reviewersCanContribute": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false,
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Column updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoardColumn"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "columnId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a column",
        "description": "Deletes a column and moves its tasks to `targetColumnId` when given, otherwise to the default column, otherwise to any remaining column. Deleting the last column is rejected (`invalidBoard`).",
        "tags": [
          "Board Columns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetColumnId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Column on the same board to move the deleted column's tasks into."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Column deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Column deleted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "columnId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/tasks": {
      "get": {
        "summary": "Get tasks for a board",
        "description": "Returns all tasks on a board, optionally filtered by column, status, assignee, search, or tags. Tasks are populated with creator, assignedTo, and project. API tokens need the `tasks:read` scope.",
        "tags": [
          "Board Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "columnId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter tasks by column"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Case-insensitive substring match on task subject"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "inProgress",
                "complete",
                "closed"
              ]
            },
            "description": "Filter by task status"
          },
          {
            "name": "assignedToId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by assignee"
          },
          {
            "name": "tags",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              },
              "x-accepts-single-value": true
            },
            "style": "form",
            "explode": true,
            "description": "Filter by one or more tag IDs (matches tasks containing any of the supplied tags)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of tasks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Task"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      },
      "post": {
        "summary": "Create a task on a board",
        "description": "Creates a new task and places it on the board. If no columnId is specified, the task\nis placed in the default column. Auto-assigns an incrementing taskNumber. API tokens\nneed the `tasks:write` scope. Reviewers can only create tasks in columns flagged\n`reviewersCanContribute` (403 `reviewerCannotContributeHere`); an assignee who cannot\nsee the board's visibility is rejected (400 `invalidAssignee`).\n\nWhen `announce` is provided, an assistant reply carrying the new task is posted in\n`announce.chatId` as a reply to `announce.messageId`; announce failures never fail\nthe request.\n",
        "tags": [
          "Board Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 20000,
                    "nullable": true
                  },
                  "assignedToId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "User to assign the task to"
                  },
                  "columnId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Column to place the task in (defaults to the default column)"
                  },
                  "announce": {
                    "type": "object",
                    "properties": {
                      "chatId": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "messageId": {
                        "type": "string",
                        "format": "uuid"
                      }
                    },
                    "required": [
                      "chatId",
                      "messageId"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "subject"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Task created on board",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/tasks/add": {
      "post": {
        "summary": "Add an existing task to a board",
        "description": "Links an existing project task to a board column. If the task does not have a taskNumber, one is assigned.",
        "tags": [
          "Board Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "taskId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The existing task to add"
                  },
                  "columnId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Column to place the task in (defaults to the default column)"
                  }
                },
                "required": [
                  "taskId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task added to board",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/tasks/{taskId}/move": {
      "put": {
        "summary": "Move a task to a different column (or board)",
        "description": "Moves a task to a new column and optionally sets its position. Pass `targetBoardId`\nto move the task onto another board in the same project; `columnId` then defaults to\nthe target board's default column and the caller must also hold `canMove*BoardTask`\nfor the target board's visibility (403 otherwise). Without `targetBoardId`,\n`columnId` is required.\n\nIf the destination column has a `taskStatus`, the task's `status` is set to that\nvalue; otherwise the status is left unchanged.\n",
        "tags": [
          "Board Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetBoardId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Move the task to this board instead of staying on `{boardId}`."
                  },
                  "columnId": {
                    "type": "string",
                    "format": "uuid",
                    "x-conditionally-required": true,
                    "description": "Target column. Required unless `targetBoardId` is given (then defaults to the target board's default column)."
                  },
                  "sortOrder": {
                    "type": "number",
                    "description": "Position within the column"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task moved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/tasks/{taskId}": {
      "delete": {
        "summary": "Remove a task from a board",
        "description": "Unlinks a task from the board by setting boardId and columnId to null. The task itself is not deleted.",
        "tags": [
          "Board Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task removed from board",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/details": {
      "put": {
        "summary": "Update task details",
        "description": "Update a task's subject, description, or assignee. Used for editing task content outside of board column operations.",
        "tags": [
          "Task Details"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 20000,
                    "nullable": true
                  },
                  "assignedToId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Set to null to unassign"
                  }
                },
                "additionalProperties": false,
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/links": {
      "post": {
        "summary": "Link two tasks",
        "description": "Creates a relationship link between two tasks. Duplicate links are rejected.",
        "tags": [
          "Task Links"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "linkedTaskId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The task to link to"
                  },
                  "linkType": {
                    "type": "string",
                    "default": "related",
                    "enum": [
                      "related",
                      "blocks",
                      "blockedBy",
                      "duplicate"
                    ]
                  }
                },
                "required": [
                  "linkedTaskId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Task link created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskLink"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "`taskLinkAlreadyExists` — these tasks are already linked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Get links for a task",
        "description": "Returns all links for a task in both directions. Link types are normalized relative to the queried task (e.g., blocks/blockedBy are flipped when the task is in the linkedTaskId position).",
        "tags": [
          "Task Links"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of task links with populated linked tasks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TaskLink"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/links/{linkedTaskId}": {
      "delete": {
        "summary": "Unlink two tasks",
        "description": "Removes the link between two tasks in both directions.",
        "tags": [
          "Task Links"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "linkedTaskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task link removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Tasks unlinked"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/follow": {
      "put": {
        "summary": "Follow a board",
        "description": "Add the current user to the board's followers list. Idempotent — following again has no effect. Board followers receive email notifications when new tasks are created.",
        "tags": [
          "Board Following"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Board with updated followers list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Board"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/unfollow": {
      "put": {
        "summary": "Unfollow a board",
        "description": "Remove the current user from the board's followers list.",
        "tags": [
          "Board Following"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Board with updated followers list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Board"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/tag": {
      "put": {
        "summary": "Tag a board",
        "description": "Add a project tag to a board. Idempotent — tagging again has no effect.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the project tag to add"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Board with updated tags",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Board"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/{boardId}/untag": {
      "put": {
        "summary": "Untag a board",
        "description": "Remove a project tag from a board.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Board with updated tags",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Board"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/boards/project/{projectId}/tasks": {
      "get": {
        "summary": "Search tasks across all boards in a project",
        "description": "Returns paginated tasks across all boards in a project. Supports search,\nboard/status/assignee/tag filters. By default only tasks that belong to a\nboard are returned; pass `boardId=unassigned` to get the orphaned (no\nboard) tasks instead.\n",
        "tags": [
          "Board Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "description": "OR-matched search. Always matches against `subject` (case-insensitive substring).\nAdditionally matches the exact `taskNumber` when the query is purely numeric,\nand the exact task `id` when the query is a UUID (or legacy 24-char hex ObjectId).\n"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "inProgress",
                "complete",
                "closed"
              ]
            }
          },
          {
            "name": "boardId",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "type": "string",
                  "enum": [
                    "unassigned"
                  ]
                }
              ]
            },
            "description": "Filter by specific board ID, or pass the literal string `unassigned` to return only tasks with no board."
          },
          {
            "name": "columnId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "assignedToId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "visibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Must be a visibility the caller can see (403 otherwise). When omitted and the caller can only see one tier, results are narrowed to it."
          },
          {
            "name": "tags",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              },
              "x-accepts-single-value": true
            },
            "style": "form",
            "explode": true,
            "description": "Filter by one or more tag IDs (matches tasks containing any of the supplied tags)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order (defaults to createdAt descending)"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of tasks across boards (populated with creator and assignedTo)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Task"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "totalPages": {
                      "type": "integer"
                    },
                    "totalResults": {
                      "type": "integer"
                    },
                    "hasNextPage": {
                      "type": "boolean"
                    },
                    "hasPrevPage": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/bots": {
      "post": {
        "summary": "Create a bot user in a workspace",
        "description": "Creates a bot user, assigns workspace membership with the supplied roles, and issues a single API key. The raw key is returned **once** in the response and is never retrievable again — store it immediately.",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Workspace ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "Display name for the bot"
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "Hex color from the approved palette. Auto-assigned if omitted."
                  },
                  "roles": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "workspaceAdmin",
                        "workspaceMember",
                        "workspaceChatMember"
                      ]
                    },
                    "minItems": 1,
                    "description": "Workspace roles to assign to the bot"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "chat:read",
                        "chat:write",
                        "tasks:read",
                        "tasks:write",
                        "assets:read",
                        "assets:write",
                        "projects:read",
                        "workspaces:read"
                      ]
                    },
                    "description": "Token scopes granted to the bot's API key. Defaults to `[]` when omitted — an empty-scope key still authenticates, but receives 403 `tokenScopeMissing` on any route that requires a token scope. Keys are immutable; to add scopes later, rotate the key."
                  }
                },
                "required": [
                  "name",
                  "roles"
                ],
                "additionalProperties": false,
                "example": {
                  "name": "Release Bot",
                  "color": "#37474F",
                  "roles": [
                    "workspaceAdmin"
                  ],
                  "scopes": [
                    "chat:read",
                    "chat:write"
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Bot user created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bot": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/BotUser"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "apiKey": {
                              "$ref": "#/components/schemas/BotApiKeySummary"
                            }
                          }
                        }
                      ]
                    },
                    "rawKey": {
                      "type": "string",
                      "description": "The full API key in the format `nrm_bot_{8-char-prefix}_{32-char-secret}`.\nReturned once at creation and never again; only a hash is stored, so the\nkey cannot be recovered later.\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden. Caller lacks `canManageBots`, or the workspace has reached the `botLimitReached` cap."
          }
        },
        "x-request-source": "joi"
      },
      "get": {
        "summary": "List bot users in a workspace",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bot users in the workspace, each with its workspace roles and project memberships.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/BotUser"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "roles": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Workspace roles held by the bot."
                          },
                          "membershipId": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "projectMemberships": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/BotProjectMembership"
                            }
                          }
                        }
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/bots/{botId}": {
      "get": {
        "summary": "Get a single bot user",
        "description": "Returns bot profile, workspace roles, and API key metadata (no secrets).",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bot details.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/BotUser"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "roles": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "membershipId": {
                          "type": "string",
                          "format": "uuid",
                          "nullable": true
                        },
                        "apiKeys": {
                          "type": "array",
                          "description": "Active API keys (no secrets).",
                          "items": {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/BotApiKeySummary"
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "lastUsed": {
                                    "type": "string",
                                    "format": "date-time",
                                    "nullable": true
                                  },
                                  "expiresAt": {
                                    "type": "string",
                                    "format": "date-time",
                                    "nullable": true
                                  },
                                  "status": {
                                    "type": "string",
                                    "enum": [
                                      "active"
                                    ]
                                  }
                                }
                              }
                            ]
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update a bot user",
        "description": "Update the bot's display name and/or color. At least one field must be supplied.",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ]
                  }
                },
                "additionalProperties": false,
                "minProperties": 1,
                "example": {
                  "name": "Release Bot v2",
                  "color": "#FF5722"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bot updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BotUser"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a bot user",
        "description": "Revokes all API keys for the bot, removes the workspace membership, and marks the user `inactive`.",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Bot deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/bots/{botId}/rotate-key": {
      "post": {
        "summary": "Rotate the bot's API key",
        "description": "Revokes the bot's current active API key and issues a new one. The new raw key is returned once.",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "New key issued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apiKey": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "keyPrefix": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "createdAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "rawKey": {
                      "type": "string",
                      "description": "The new full API key. Returned once."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Bot has no active API key to rotate."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/bots/{botId}/avatar": {
      "post": {
        "summary": "Set the bot's avatar",
        "description": "Creates an avatar asset for the bot and returns signed multipart upload URLs; the caller then uploads the image bytes directly to those URLs. The returned `user` carries the new (still `pendingUpload`) avatar asset so the client can render it optimistically; the processed thumbnail arrives via the `userAvatarUpdate` WebSocket event.",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "File name including extension."
                  },
                  "sizeInMB": {
                    "type": "number",
                    "minimum": 0,
                    "description": "File size in MB; drives the number of signed part URLs."
                  },
                  "checksum": {
                    "type": "string",
                    "description": "Hash of the file content."
                  }
                },
                "required": [
                  "name",
                  "sizeInMB",
                  "checksum"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Avatar asset created; upload the bytes to `signedUrlData`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AssetAndSignedLink"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "user": {
                          "$ref": "#/components/schemas/BotUser"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Bot not found in this workspace."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/bots/{botId}/memberships": {
      "get": {
        "summary": "List the bot's project memberships",
        "description": "Project memberships of the bot, limited to projects that belong to this workspace.",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Project memberships.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BotProjectMembership"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Bot not found in this workspace."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/bots/{botId}/memberships/project/{projectId}": {
      "put": {
        "summary": "Add the bot to a project or replace its project roles",
        "description": "Upserts the bot's membership on the project. If a membership already exists its role list is replaced. `projectOwner` and `reviewerAdmin` cannot be granted to bots.",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "roles": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "creator",
                        "reviewer",
                        "projectAdmin"
                      ]
                    },
                    "minItems": 1
                  }
                },
                "required": [
                  "roles"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Membership created or updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BotProjectMembership"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Bot not found in this workspace, or project not found in this workspace."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Must belong to `workspaceId`."
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Remove the bot from a project",
        "tags": [
          "Bots"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Membership removed (also succeeds when no membership existed)."
          },
          "404": {
            "description": "Bot not found in this workspace, or project not found in this workspace."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Must belong to `workspaceId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/topic": {
      "post": {
        "summary": "Create a new topic chat",
        "tags": [
          "Chats"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "topicType": {
                    "type": "string",
                    "enum": [
                      "project",
                      "asset"
                    ]
                  },
                  "topicId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "subject": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "visibility": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "description": "No field is enforced as required by validation, but `topicType` and `topicId` are needed to create a chat."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Topic chat created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Chat"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/chats/member": {
      "post": {
        "summary": "Create a new member chat.",
        "tags": [
          "Chats"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scopeType": {
                    "type": "string",
                    "enum": [
                      "workspace"
                    ]
                  },
                  "scopeId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "subject": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "memberIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "One of the platform's approved hex colours."
                  }
                },
                "additionalProperties": false,
                "description": "Only workspace (\"Team Chat\") member chats may be created; project-scoped member chats are deprecated. No field is enforced as required by validation, but `scopeType`, `scopeId` and `memberIds` are needed to create a chat. When `color` is omitted a random approved color is assigned.\n"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Private chat created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          },
          "400": {
            "description": "Bad request due to validation failure."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Get a paginated list of member chats the user has created or is part of.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "scopeId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only return member chats belonging to this scope (workspace or project)."
          },
          {
            "name": "subjectSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Search term to filter chats by subject (partial match, case-insensitive)."
          },
          {
            "name": "memberSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Member ID to filter chats by specific member participation."
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type to use"
          },
          {
            "name": "updatedBefore",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time",
              "x-conditionally-required": true
            },
            "description": "Get chats updated before this timestamp (only for index pagination)."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "updatedAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for chats based on creation or update times."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 20,
              "default": 20
            },
            "description": "Limit the number of chats returned."
          },
          {
            "name": "recentMessages",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 20,
              "default": 20
            },
            "description": "Limit the number of recent messages returned for each chat."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number for index pagination."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only for cursor pagination)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only for cursor pagination)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only for cursor pagination)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include cursor record in results (only for cursor pagination)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only for cursor pagination)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include the startAt record in the results (only for cursor pagination)"
          },
          {
            "name": "archived",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Archive filter for the current user. `true` returns only chats the user has archived, `false` returns only chats the user has not archived. Omit to return both.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "List of member chats retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/IndexPaginatedChatMembers"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedChatMembers"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request due to validation failure."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/member/{chatId}": {
      "get": {
        "summary": "Get member chat by id.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved member chat details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update member chat subject and/or color.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "The new subject for the chat."
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "One of the platform's approved hex colours."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated member chat subject.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Mark member chat for deletion. From the user's perspective the chat will have been deleted. Only chat creators may delete a chat.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat successfully marked as deleted."
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/member/{chatId}/icon": {
      "put": {
        "summary": "Upload and set member chat icon.",
        "description": "Creates an icon asset for the member chat and returns signed upload links for it. Any existing icon is marked for deletion. Requires member-chat management permission (`canManageMemberChat`).\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the member chat"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "File name with extension",
                    "example": "icon.png"
                  },
                  "checksum": {
                    "type": "string",
                    "description": "MD5 or SHA-256 hash of the file",
                    "example": "d9729feb74992cc3482b350163a1a010"
                  },
                  "sizeInMB": {
                    "type": "number",
                    "maximum": 10,
                    "description": "File size in megabytes",
                    "example": 0.25
                  }
                },
                "required": [
                  "name",
                  "checksum",
                  "sizeInMB"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Icon asset created. The response is the created asset/upload-link record with the updated chat attached under `chat`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AssetAndSignedLink"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "chat": {
                          "$ref": "#/components/schemas/ChatMember"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/member/{chatId}/archive": {
      "put": {
        "summary": "Archive a member chat for the current user.",
        "description": "Adds the current user's ID to the chat's archivedBy array. Per-user archiving does not affect other members' view of the chat.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat archived successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - User is not a member of the chat."
          },
          "404": {
            "description": "Chat not found."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/member/{chatId}/unarchive": {
      "put": {
        "summary": "Unarchive a member chat for the current user.",
        "description": "Removes the current user's ID from the chat's archivedBy array.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat unarchived successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - User is not a member of the chat."
          },
          "404": {
            "description": "Chat not found."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/member/addable": {
      "get": {
        "summary": "Get members addable to a NEW member chat, by scope, before the chat exists.",
        "description": "Populates the create-chat member picker. Unlike the raw membership-list endpoints, this is not admin-gated — any user permitted to create a member chat in the scope (including a plain workspaceChatMember) may call it, and only chat-eligible members are returned.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "scopeType",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "workspace",
                "project"
              ]
            }
          },
          {
            "name": "scopeId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of members addable to a member chat created in the given scope.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ProjectScopeResponse"
                    },
                    {
                      "$ref": "#/components/schemas/WorkspaceScopeResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "403": {
            "description": "Forbidden - User cannot create a member chat in the scope."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/member/members/{chatId}": {
      "get": {
        "summary": "Get a list of addable members to a member chat based on the chats scope and the users role in that scope.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of addable members based on the chat's scope and the user's role.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ProjectScopeResponse"
                    },
                    {
                      "$ref": "#/components/schemas/WorkspaceScopeResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Add an array of members to a member chat by user ID.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "memberIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Array of user IDs."
                  }
                },
                "additionalProperties": false
              }
            }
          },
          "description": "Array of user IDs to add as members. Not enforced by validation, but the request cannot succeed without it."
        },
        "responses": {
          "200": {
            "description": "Successfully added members to the chat.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete an array of members from a member chat by user ID.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "memberIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Array of user IDs."
                  }
                },
                "additionalProperties": false
              }
            }
          },
          "description": "Array of user IDs to remove from the chat. Not enforced by validation, but the request cannot succeed without it. The chat creator cannot be removed (`ownerCannotLeaveChat`)."
        },
        "responses": {
          "200": {
            "description": "Successfully removed members from the chat.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}": {
      "get": {
        "summary": "Get topic chat by id.",
        "description": "Returns a topic chat (project/asset/task/public) without messages. Member chats are served by `GET /chats/member/{chatId}`.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved chat details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Chat"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update chat subject.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "The new subject for the chat."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated chat subject.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Chat"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Mark chat for deletion. Only chat creators may delete a chat.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat successfully marked as deleted."
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/follow": {
      "put": {
        "summary": "Follow a chat to receive updates",
        "description": "Add the authenticated user to the chat's following list. Users in the following list receive notifications about new messages and updates. Works with all chat types (topic, member, submission). Following a chat multiple times is idempotent.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat to follow"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully followed the chat"
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "403": {
            "description": "Forbidden - User does not have permission to access this chat"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/unfollow": {
      "put": {
        "summary": "Unfollow a chat to stop receiving updates",
        "description": "Remove the authenticated user from the chat's following list. Users can unfollow a chat even if they no longer have access to it, allowing them to stop receiving notifications. Works with all chat types (topic, member, submission). Unfollowing a chat you're not following is idempotent.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat to unfollow"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully unfollowed the chat"
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/mentionable/assets": {
      "get": {
        "summary": "Get mentionable assets for a chat with pagination",
        "description": "Returns a paginated list of assets that can be mentioned in the specified chat. The assets returned depend on the chat type:\n- **Topic chat about a project**: Returns assets from that project with matching visibility\n- **Topic chat about an asset**: Returns assets from the asset's owner project with matching visibility\n- **Member chat with project scope**: Returns all assets from that project (all visibilities)\n- **Submission chat**: Returns all assets from the project (all visibilities)\n- **AI chat (Nu Chat)**: Returns assets from the topic's project, filtered to the\n  **caller's own** visibility tiers. A workspace- or social-scoped topic has no\n  project to resolve against and returns an empty array.\n\n  Unlike every other chat type above, an AI chat has no meaningful visibility of\n  its own to inherit — its backing `Chat` row is created with a fixed `creator`\n  placeholder, identical for every user — so tiers are derived from the caller's\n  inherited permissions instead. Owning the topic is not on its own evidence of\n  creator-tier access to the project.\n- **Member chat with workspace/social scope**: Returns empty array (no assets are mentionable)\n\nOnly active assets are returned. Supports both cursor-based and index-based pagination, plus name filtering.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional partial name search filter (case-insensitive)"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "name": 1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order (1 for ascending, -1 for descending)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of results per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type (cursor or index-based)"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for cursor-based pagination (only when paginate=cursor)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Reverse pagination direction (only when paginate=cursor)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include total counts (only when paginate=cursor)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include cursor record (only when paginate=cursor)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only when paginate=index)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved mentionable assets. Index pagination returns a `PaginatedResult`; cursor pagination returns a `CursorPaginatedResult`. Both carry the matching assets in `results`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaginatedResult"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Asset"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/CursorPaginatedResult"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Asset"
                              }
                            }
                          }
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "403": {
            "description": "Forbidden - User does not have permission to access this chat"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/mentionable/folders": {
      "get": {
        "summary": "Get mentionable folders for a chat with pagination",
        "description": "Returns a paginated list of folders that can be mentioned in the specified chat. The folders returned depend on the chat type:\n- **Topic chat about a project**: Returns folders from that project with matching visibility\n- **Topic chat about an asset**: Returns folders from the asset's owner project with matching visibility\n- **Member chat with project scope**: Returns all folders from that project (all visibilities)\n- **Submission chat**: Returns reviewer-visibility folders from the project\n- **AI chat (Nu Chat)**: Returns folders from the topic's project, filtered to the\n  **caller's own** visibility tiers. A workspace- or social-scoped topic has no\n  project to resolve against and returns an empty array.\n\n  Unlike every other chat type above, an AI chat has no meaningful visibility of\n  its own to inherit — its backing `Chat` row is created with a fixed `creator`\n  placeholder, identical for every user — so tiers are derived from the caller's\n  inherited permissions instead. Owning the topic is not on its own evidence of\n  creator-tier access to the project.\n- **Member chat with workspace/social scope**: Returns empty array (no folders are mentionable)\n\nOnly active folders are returned. Supports both cursor-based and index-based pagination, plus name filtering.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional partial name search filter (case-insensitive)"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "name": 1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order (1 for ascending, -1 for descending)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of results per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type (cursor or index-based)"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for cursor-based pagination (only when paginate=cursor)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Reverse pagination direction (only when paginate=cursor)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include total counts (only when paginate=cursor)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include cursor record (only when paginate=cursor)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only when paginate=index)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved mentionable folders. Index pagination returns a `PaginatedResult`; cursor pagination returns a `CursorPaginatedResult`. Both carry the matching folders in `results`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaginatedResult"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Folder"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/CursorPaginatedResult"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Folder"
                              }
                            }
                          }
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "403": {
            "description": "Forbidden - User does not have permission to access this chat"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/mentionable/tasks": {
      "get": {
        "summary": "Get mentionable board tasks for a chat with pagination",
        "description": "Returns a paginated list of board tasks that can be mentioned in the specified chat. Requires the workspace to have the `boards` capability.\n\nScope/visibility rules:\n- **Topic chat about a project**: Tasks in that project on boards whose `visibility` array includes the chat's visibility.\n- **Topic chat about an asset**: Tasks in the asset's owner project on boards whose `visibility` array includes the chat's visibility.\n- **Topic chat about a task**: Sibling tasks in the parent task's project on boards whose `visibility` array includes the chat's visibility.\n- **Member chat with project scope**: All board tasks in that project (no visibility filter — mirrors asset/folder mention behaviour for this chat type).\n- **Submission chat**: Tasks in the submission's project on boards whose `visibility` array includes `'reviewer'`.\n- **Member chat with workspace/social scope**: Empty result — no tasks are mentionable.\n\nLegacy mention-only tasks (`boardId === null`) are never returned. The `nameSearch` query is OR-matched across `subject` substring, exact `taskNumber` (when numeric), and exact `id` (when a UUID).\n\nReturns `capabilityNotAvailable` / 403 when the workspace lacks the `boards` capability.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional search filter — matches subject substring (case-insensitive), exact taskNumber, or exact task id"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "subject": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "taskNumber": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "updatedAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order (1 for ascending, -1 for descending). Defaults to most recently updated first."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of results per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type (cursor or index-based)"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for cursor-based pagination (only when paginate=cursor)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Reverse pagination direction (only when paginate=cursor)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include total counts (only when paginate=cursor)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include cursor record (only when paginate=cursor)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only when paginate=index)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved mentionable tasks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "$ref": "#/components/schemas/UUID"
                          },
                          "subject": {
                            "type": "string"
                          },
                          "taskNumber": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "inProgress",
                              "complete",
                              "closed"
                            ]
                          },
                          "projectId": {
                            "$ref": "#/components/schemas/UUID"
                          },
                          "boardId": {
                            "$ref": "#/components/schemas/UUID"
                          },
                          "assignedToId": {
                            "$ref": "#/components/schemas/UUID"
                          },
                          "createdAt": {
                            "type": "integer",
                            "format": "int64"
                          },
                          "updatedAt": {
                            "type": "integer",
                            "format": "int64"
                          }
                        }
                      }
                    },
                    "page": {
                      "type": "integer",
                      "description": "Current page (index pagination only)"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "totalResults": {
                      "type": "integer"
                    },
                    "totalPages": {
                      "type": "integer"
                    },
                    "hasNext": {
                      "type": "boolean",
                      "description": "Cursor pagination indicator"
                    },
                    "hasPrev": {
                      "type": "boolean",
                      "description": "Cursor pagination indicator"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "403": {
            "description": "Forbidden - User does not have permission to access this chat, or the workspace lacks the `boards` capability (`capabilityNotAvailable`)"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/mentionable/submissions": {
      "get": {
        "summary": "Get mentionable submissions for a chat",
        "description": "Returns a paginated list of active submissions in the chat's project that can be mentioned with `{{submissionMention:submissionId}}` tokens.\nThe project is resolved from the chat (project/asset/task topic chats, project-scoped member chats, submission chats and project-scoped AI chats).\nChats without a project scope return an empty page. Index pagination only.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional partial subject search filter (case-insensitive)"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "subject": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "lastMessageAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "createdAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order (1 for ascending, -1 for descending)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of results per page"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "default": 1
            },
            "description": "Page number"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved mentionable submissions",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedResult"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MentionableSubmission"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/mentionable/publics": {
      "get": {
        "summary": "Get mentionable public releases for a chat",
        "description": "Returns a paginated list of active public releases (share links) owned by the chat's project that can be mentioned with\n`{{publicMention:token}}` tokens. The project is resolved from the chat the same way as for mentionable submissions.\nChats without a project scope return an empty page. Index pagination only.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional partial title search filter (case-insensitive)"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "title": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "expires": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "createdAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order (1 for ascending, -1 for descending)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Maximum number of results per page"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "default": 1
            },
            "description": "Page number"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved mentionable public releases",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedResult"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MentionablePublic"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/topic/{topicId}": {
      "get": {
        "summary": "Get chat by Topic ID",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "topicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier for the topic"
          },
          {
            "name": "topicType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "project",
                "public",
                "task"
              ]
            },
            "description": "Type of the chat topic"
          },
          {
            "name": "visibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Visibility of the chat"
          },
          {
            "name": "messages",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of recent messages to retrieve. Note - the current handler does not forward this value, so the default of 10 is always applied."
          },
          {
            "name": "replies",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of recent replies to retrieve"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting options for the chat"
          }
        ],
        "responses": {
          "200": {
            "description": "Chat data retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Chat"
                }
              }
            }
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/new-message": {
      "post": {
        "summary": "Create a new message in the specified chat",
        "description": "Creates a message in any chat type (topic, member, submission, AI, support). Either `content` or at least one attachment must be provided. API tokens require the `chat:write` scope (`tokenScopeMissing` / 403 otherwise). Mentioning users in topic/member/submission chats creates tasks and notifications for them.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the chat"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "maxLength": 10000
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/IdNameOrScratchIdNameUnion"
                    },
                    "maxItems": 6,
                    "description": "Attachments for a new message. Two item shapes are accepted, distinguished by the presence of `scratchId`: the upload shape describes new bytes the client will upload via the signed URLs returned in `attachmentData`; the scratch shape references bytes already staged on Scratch (e.g. an AI Revision result) which are promoted to chat-scoped assets when the message lands.\n"
                  },
                  "replyToId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "annotations": {
                    "type": "array",
                    "items": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/DotAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/FrameCommentAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/ShapeAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/TextAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/PathAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/Dot3dAnnotationInput"
                        }
                      ],
                      "title": "AnnotationInput"
                    },
                    "maxItems": 100,
                    "description": "Annotations associated with the message; each is either a Dot or a FrameComment."
                  },
                  "mentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of user IDs mentioned in the message using {{mention:userId}} tokens"
                  },
                  "assetMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of asset IDs mentioned in the message using {{assetMention:assetId}} tokens. In responses, these are populated with full Asset objects."
                  },
                  "folderMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of folder IDs mentioned in the message using {{folderMention:folderId}} tokens. In responses, these are populated with full Folder objects."
                  },
                  "submissionMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of submission IDs mentioned in the message using {{submissionMention:submissionId}} tokens."
                  },
                  "publicMentions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of public release tokens mentioned in the message using {{publicMention:token}} tokens."
                  },
                  "taskMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of board task IDs mentioned in the message using {{taskMention:taskId}} tokens. Requires the workspace to have the `boards` capability. In responses, these are populated with task summaries (id, subject, taskNumber, status, boardId, boardName, visibility).\n"
                  },
                  "quotes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 5,
                    "default": [],
                    "description": "Array of message IDs to quote in this message. Quoted messages are referenced using {{quote:messageId}} tokens in content.\n\n**Placement Rules:**\n- **Topic to Topic**: Can quote from topic chats with the same visibility within the same project\n- **Topic to Submission**: Only reviewer visibility topic chats can be quoted in submissions (same project)\n- **Submission to Topic/Member**: Can quote submission messages to topic or member chats within the same project\n- **Member chats**: Messages from member chats cannot be quoted\n\n**Permission Requirements:**\n- User must have permission to view the quoted message\n- For topic chats: requires canGetCreatorChat or canGetReviewerChat permission based on visibility\n- For submission chats: requires canGetSubmission permission\n"
                  },
                  "linkPreviews": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/SharedUrlTitleDescription"
                    },
                    "maxItems": 5,
                    "default": [],
                    "description": "Link preview metadata for URLs in the content."
                  },
                  "pageContext": {
                    "type": "object",
                    "properties": {
                      "path": {
                        "type": "string",
                        "maxLength": 2000
                      },
                      "pageTitle": {
                        "type": "string",
                        "maxLength": 500
                      },
                      "visibleAssetIds": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "maxItems": 20,
                        "description": "Assets on screen when the message was sent. Re-read server-side under the caller's visibility tiers; ids they cannot see are dropped.\n"
                      },
                      "contextItems": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "asset",
                                "folder",
                                "user",
                                "submission",
                                "public"
                              ]
                            },
                            "id": {
                              "type": "string",
                              "maxLength": 200
                            }
                          },
                          "required": [
                            "type",
                            "id"
                          ],
                          "additionalProperties": false
                        },
                        "maxItems": 20,
                        "description": "Context-column pins, as a FALLBACK only. The topic's own stored `contextItems` (see `PATCH /v1/ai/chat/topics/{topicId}`) take precedence whenever it has any — persisting them there is what makes the Context column survive a reload. Identity only; every entry is re-resolved under the caller's live permissions on each turn, and anything they cannot see is silently omitted.\n"
                      },
                      "recentActions": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "maxLength": 200
                        },
                        "maxItems": 50,
                        "description": "One-line summaries of the sender's recent actions in this project."
                      }
                    },
                    "additionalProperties": false,
                    "description": "Snapshot of what the sender is looking at, used only when the target chat is an AI chat (`chatType: 'ai'`); every other chat type ignores it. Rendered into that turn's system prompt and never persisted on the message.\n"
                  }
                },
                "additionalProperties": false,
                "description": "Either `content` or at least one item in `attachments` must be provided."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message created successfully. If upload-shape attachments were included a key called attachmentData will be included with asset and signed link data for the newly created assets.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ChatMessage"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "attachmentData": {
                          "$ref": "#/components/schemas/AssetAndSignedLinks"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters (including missing content/attachments and invalid quotes)"
          },
          "403": {
            "description": "API token lacks the `chat:write` scope, or the quoted message may not be quoted here"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/asset/{assetId}/{visibility}/new-message": {
      "post": {
        "summary": "Create a new message in an asset chat, creating the chat if it doesn't exist",
        "description": "Creates a new message in an asset chat. If the chat doesn't exist for the specified visibility, it will be created automatically. Only works with media assets.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to which the chat belongs."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Visibility level of the chat"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "maxLength": 10000,
                    "description": "The message content"
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/IdNameOrScratchIdNameUnion"
                    },
                    "maxItems": 6,
                    "description": "Attachments for a new message. Two item shapes are accepted, distinguished by the presence of `scratchId`: the upload shape describes new bytes the client will upload via the signed URLs returned in `attachmentData`; the scratch shape references bytes already staged on Scratch (e.g. an AI Revision result) which are promoted to chat-scoped assets when the message lands.\n"
                  },
                  "replyToId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "annotations": {
                    "type": "array",
                    "items": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/DotAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/FrameCommentAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/ShapeAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/TextAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/PathAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/Dot3dAnnotationInput"
                        }
                      ],
                      "title": "AnnotationInput"
                    },
                    "maxItems": 100,
                    "description": "Array of annotations (dots or frame comments)"
                  },
                  "mentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of user IDs mentioned in the message using {{mention:userId}} tokens"
                  },
                  "assetMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of asset IDs mentioned in the message using {{assetMention:assetId}} tokens. In responses, these are populated with full Asset objects."
                  },
                  "folderMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of folder IDs mentioned in the message using {{folderMention:folderId}} tokens. In responses, these are populated with full Folder objects."
                  },
                  "submissionMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of submission IDs mentioned in the message using {{submissionMention:submissionId}} tokens."
                  },
                  "publicMentions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of public release tokens mentioned in the message using {{publicMention:token}} tokens."
                  },
                  "quotes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 5,
                    "default": [],
                    "description": "Array of message IDs to quote. Same placement rules apply as for `CreateMessage.quotes`."
                  },
                  "messages": {
                    "type": "number",
                    "maximum": 100,
                    "default": 10,
                    "description": "Accepted for backwards compatibility. The handler does not forward it, so 10 recent messages are always returned with the chat."
                  },
                  "replies": {
                    "type": "number",
                    "maximum": 100,
                    "default": 10,
                    "description": "Accepted for backwards compatibility. The handler does not forward it, so 10 replies per message are always returned."
                  },
                  "sort": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "createdAt": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "updatedAt": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      }
                    },
                    "additionalProperties": false,
                    "default": {
                      "id": -1
                    },
                    "description": "Accepted for backwards compatibility. The handler does not forward it, so `{ id: -1 }` is always used."
                  }
                },
                "additionalProperties": false
              }
            }
          },
          "description": "Either `content` or at least one attachment must be provided."
        },
        "responses": {
          "200": {
            "description": "Message created successfully. The chat is re-read after the message lands so `recentMessages` includes it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chat": {
                      "$ref": "#/components/schemas/Chat"
                    },
                    "message": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ChatMessage"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "attachmentData": {
                              "allOf": [
                                {
                                  "$ref": "#/components/schemas/AssetAndSignedLinks"
                                }
                              ],
                              "description": "Present only if upload-shape attachments were included in the request"
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - Invalid input or missing required content/attachments"
          },
          "403": {
            "description": "Forbidden - User does not have permission to create messages in this chat"
          },
          "404": {
            "description": "Asset not found, or the asset is not a media asset (`assetInvalidFunctionType`)"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/messages": {
      "get": {
        "summary": "Get messages for a chat",
        "description": "Returns active messages for a chat with recent replies and populated attachments/mentions. API tokens require the `chat:read` scope (`tokenScopeMissing` / 403 otherwise). `createdBefore` / `createdAfter` are only accepted with index pagination.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for messages"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of messages per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type to use"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include cursor record in results (only for cursor pagination)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only for cursor pagination)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include the startAt record in the results (only for cursor pagination)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number for index pagination"
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the upper bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "createdAfter",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the lower bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "replies",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Deprecated alias of `replyLimit`. When set to a value other than 10 it takes precedence over `replyLimit`."
          },
          {
            "name": "replyLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of replies to include per message"
          },
          {
            "name": "replySort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for replies"
          },
          {
            "name": "excludeReplies",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Exclude replies from the results"
          },
          {
            "name": "highlighted",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Filter to highlighted (true) or non-highlighted (false) messages"
          },
          {
            "name": "contentSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Case-insensitive substring search on message content"
          },
          {
            "name": "authorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only return messages by this author"
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "system"
              ]
            },
            "description": "Only return user or system messages"
          },
          {
            "name": "hasAttachments",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Only return messages with (true) or without (false) attachments"
          },
          {
            "name": "hasAnnotations",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Only return messages with (true) or without (false) annotations"
          },
          {
            "name": "isConvoMessage",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Only return convo system messages (true) or exclude them (false)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved messages",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/IndexPaginatedMessages"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedMessages"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}": {
      "get": {
        "summary": "Get message by ID",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the message"
          },
          {
            "name": "replies",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of recent replies to include with the message"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order for the included replies"
          }
        ],
        "responses": {
          "200": {
            "description": "Message data retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "404": {
            "description": "Message not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Revise message by ID",
        "description": "Revise a message by ID. If annotations are provided, they will replace current annotations not merged. If no annotations are provided, current annotations will be preserved. If an empty array is provided, all annotations will be removed.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "maxLength": 10000
                  },
                  "mentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of user IDs mentioned in the message using {{mention:userId}} tokens"
                  },
                  "assetMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of asset IDs mentioned in the message using {{assetMention:assetId}} tokens. In responses, these are populated with full Asset objects."
                  },
                  "folderMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of folder IDs mentioned in the message using {{folderMention:folderId}} tokens. In responses, these are populated with full Folder objects."
                  },
                  "submissionMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of submission IDs mentioned in the message using {{submissionMention:submissionId}} tokens."
                  },
                  "publicMentions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of public release tokens mentioned in the message using {{publicMention:token}} tokens."
                  },
                  "taskMentions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 10,
                    "default": [],
                    "description": "Array of board task IDs mentioned in the message using {{taskMention:taskId}} tokens. Requires the workspace to have the `boards` capability. Tasks must be in scope for the chat — see `/chats/{chatId}/mentionable/tasks` for the scoping rules. In responses, these are populated with task summaries (id, subject, taskNumber, status, boardId, boardName, visibility).\n"
                  },
                  "quotes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "maxItems": 5,
                    "default": [],
                    "description": "Array of message IDs to quote. Same placement rules apply as when creating messages."
                  },
                  "annotations": {
                    "type": "array",
                    "items": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/DotAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/FrameCommentAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/ShapeAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/TextAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/PathAnnotationInput"
                        },
                        {
                          "$ref": "#/components/schemas/Dot3dAnnotationInput"
                        }
                      ],
                      "title": "AnnotationInput"
                    },
                    "maxItems": 100
                  },
                  "linkPreviews": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/SharedUrlTitleDescription"
                    },
                    "maxItems": 5,
                    "default": [],
                    "description": "Link preview metadata for URLs in the content. Replaces the stored previews."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message revised successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (invalid input)"
          },
          "404": {
            "description": "Message not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a message",
        "description": "Marks the message for deletion. It is removed later by the cleanup service.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the message"
          }
        ],
        "responses": {
          "200": {
            "description": "Message marked for deletion. Returns the deleted message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Message not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}/replies": {
      "get": {
        "summary": "Get replies for a message",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for replies"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of replies per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type to use"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include cursor record in results (only for cursor pagination)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only for cursor pagination)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include the startAt record in the results (only for cursor pagination)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number for index pagination"
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the upper bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "createdAfter",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the lower bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved replies",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/IndexPaginatedReplies"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedReplies"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}/attachment": {
      "post": {
        "summary": "Attach a file to a message.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the message to attach a file to"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "maximum": 10,
                      "description": "Client-side counter used to match request items with response items.",
                      "example": 1
                    },
                    "name": {
                      "type": "string",
                      "pattern": "\\.(jpg|jpeg|png|gif|tiff|bmp|webp|svg|heif|ico|raw|exr|heic|mp4|avi|mov|wmv|flv|mkv|webm|m4v|mpg|mpeg|rm|vob|3gp|ogv|ts|m2ts|hevc|divx)$",
                      "description": "File name; must end with a supported image or video extension.",
                      "example": "photo.jpg"
                    },
                    "checksum": {
                      "type": "string",
                      "pattern": "^[a-f0-9]{32,64}$",
                      "description": "MD5 or SHA-256 hash of the file content."
                    },
                    "sizeInMB": {
                      "type": "number",
                      "minimum": 0,
                      "x-exclusiveMinimum": true,
                      "example": 1.2
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "checksum",
                    "sizeInMB"
                  ],
                  "additionalProperties": false
                },
                "maxItems": 6,
                "description": "Files to attach to an existing message. Only image and video extensions are accepted."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Attachment assets created and appended to the message. Returns asset and signed upload link data for each file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetAndSignedLinks"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters"
          },
          "404": {
            "description": "Message not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}/attachment/{assetId}": {
      "delete": {
        "summary": "Remove an attachment from a message",
        "description": "Removes the asset from the message's attachments and marks the asset for deletion. If the message has no content and no attachments left afterwards, the message itself is marked for deletion and the deleted message is returned.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the message"
          },
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the attached asset to remove"
          }
        ],
        "responses": {
          "200": {
            "description": "Attachment removed. Returns the updated (or deleted) message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "404": {
            "description": "Message or asset not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}/reaction": {
      "post": {
        "summary": "Create or update a reaction on a chat message",
        "description": "Creates a new reaction or replaces an existing reaction from the same user on a chat message. Each user can only have one reaction per message.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the message to react to"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emoji": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10,
                    "description": "The emoji character(s) to use for the reaction",
                    "example": "👍"
                  }
                },
                "required": [
                  "emoji"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reaction created or updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - Invalid emoji format or missing emoji field"
          },
          "403": {
            "description": "Forbidden - User does not have permission to react to this message"
          },
          "404": {
            "description": "Message not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Remove a user's reaction from a chat message",
        "description": "Removes the authenticated user's reaction from the specified chat message. Only the user who created the reaction can remove it.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the message to remove reaction from"
          }
        ],
        "responses": {
          "200": {
            "description": "Reaction removed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - User does not have permission to remove reactions from this message"
          },
          "404": {
            "description": "Message not found or user has no reaction on this message"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}/highlight": {
      "put": {
        "summary": "Highlight a chat message",
        "description": "Sets `highlighted = true` on the message and records the highlighter. For project and asset topic chats a system message is posted in the project chat of the same visibility, a `chatHighlightMessage` notification is sent and project members are emailed. Requires the `canHighlightMessage` permission on the chat.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the message"
          }
        ],
        "responses": {
          "200": {
            "description": "Message highlighted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "404": {
            "description": "Message not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}/unhighlight": {
      "put": {
        "summary": "Remove the highlight from a chat message",
        "description": "Sets `highlighted = false`, clears the highlighter fields and removes the associated system messages and notification. Requires the `canHighlightMessage` permission on the chat.",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the message"
          }
        ],
        "responses": {
          "200": {
            "description": "Message unhighlighted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "404": {
            "description": "Message not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/mentions": {
      "get": {
        "summary": "Get messages where the user was mentioned",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter mentions by chat ID"
          },
          {
            "name": "authorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter mentions by author ID"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of results per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type to use"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only for cursor pagination)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to paginate in reverse order (only for cursor pagination)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include total counts in response (only for cursor pagination)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include cursor record in results (only for cursor pagination)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only for cursor pagination)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include the startAt record in the results (only for cursor pagination)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only for index pagination)"
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "x-conditionally-required": true
            },
            "description": "Get mentions created before this timestamp (only for index pagination)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of messages where user was mentioned",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/IndexPaginatedMessages"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedMessages"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/workspace/{workspaceId}/project-chats": {
      "get": {
        "summary": "Get every project topic chat the user can access in a workspace",
        "description": "Returns the creator/reviewer project topic chats across all projects in the workspace that the caller can read, with the latest message and message count for each. Access is derived per project from the caller's inherited permissions (`canGetCreatorChat` / `canGetReviewerChat`). Requires `canGetWorkspace` on the workspace. Results are ordered by most recent activity.\n",
        "tags": [
          "Chats"
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The unique identifier of the workspace"
          }
        ],
        "responses": {
          "200": {
            "description": "Accessible project chats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WorkspaceProjectChat"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/config": {
      "get": {
        "summary": "Get current platform configuration.",
        "tags": [
          "Config"
        ],
        "description": "Returns the current platform configuration: validation limits, approved colours, roles and their permissions, billable roles, supported / restricted file types, supported media types per asset function, and feature flags for convos and link previews. Public — no authentication required.\n",
        "responses": {
          "200": {
            "description": "Current platform configuration.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Config"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "rolesAndPermissions": {
                          "type": "object",
                          "description": "Map of role name to its metadata and permission list.",
                          "additionalProperties": true
                        },
                        "billableRoles": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Roles that count toward billing."
                        },
                        "supportedFileTypes": {
                          "type": "object",
                          "description": "Supported file extensions / mime types, keyed by media type.",
                          "additionalProperties": true
                        },
                        "restrictedFileTypes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "File types that are never accepted for upload."
                        },
                        "supportedMediaTypes": {
                          "type": "object",
                          "description": "Allowed media types per asset function type.",
                          "properties": {
                            "attachment": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "example": [
                                "image",
                                "video",
                                "audio",
                                "file",
                                "3d",
                                "document"
                              ]
                            },
                            "media": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "avatar": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "example": [
                                "image"
                              ]
                            },
                            "logo": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "icon": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "convos": {
                          "type": "object",
                          "properties": {
                            "enabled": {
                              "type": "boolean",
                              "description": "Whether the Convos (voice/video) feature is enabled on this deployment."
                            }
                          }
                        },
                        "linkPreview": {
                          "type": "object",
                          "properties": {
                            "enabled": {
                              "type": "boolean"
                            },
                            "provider": {
                              "type": "string",
                              "description": "Link preview provider in use."
                            },
                            "externalUrl": {
                              "type": "string",
                              "nullable": true,
                              "description": "External preview service URL, when an external provider is configured."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/convos": {
      "post": {
        "summary": "Start a new convo",
        "description": "Start a new video or audio convo in a chat. If the chat already has an active convo it is auto-completed\nbefore the new one is created. Creates a Daily.co room and posts a system message in the chat.\nThe starter automatically joins as an active participant and receives a meeting token.\n\nGates (in order): convos feature flag (403 `featureDisabled`), active workspace subscription\n(400 `subscriptionNotActive`), the workspace `convos` capability (403 `capabilityNotAvailable`),\na positive credit balance (400 `creditBalanceInsufficient`) and the same chat permission required to post a message\nin the chat (`canStartConvo`).\n",
        "tags": [
          "Convos"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "chatId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "chatType": {
                    "type": "string",
                    "enum": [
                      "topic",
                      "member",
                      "submission"
                    ],
                    "description": "Type of chat (topic for projects/assets, member for direct messages, submission for review chats). Stored on the convo as `chat` / `chatMember` / `chatSubmission`."
                  },
                  "convoType": {
                    "type": "string",
                    "enum": [
                      "video",
                      "audio"
                    ],
                    "description": "Type of convo (video call or audio only)"
                  },
                  "sendEmailNotification": {
                    "type": "boolean",
                    "default": false,
                    "description": "When true, chat participants are emailed that a convo started."
                  },
                  "subject": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Optional subject, appended to the start system message."
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 5000,
                    "description": "Optional notes, appended to the start system message."
                  }
                },
                "required": [
                  "chatId",
                  "chatType",
                  "convoType"
                ],
                "additionalProperties": false,
                "example": {
                  "chatId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
                  "chatType": "topic",
                  "convoType": "video",
                  "sendEmailNotification": false,
                  "subject": "Design review"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Convo created successfully. The convo is returned with a Daily.co meeting token for the starter.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Convo"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "meetingToken": {
                          "type": "string",
                          "description": "Daily.co meeting token for the starter (admin + record privileges)."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error, no active subscription (`subscriptionNotActive`) or insufficient credits (`creditBalanceInsufficient`)"
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`), workspace lacks the `convos` capability (`capabilityNotAvailable`), or the user may not post in the chat"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/convos/{convoId}": {
      "get": {
        "summary": "Get convo details",
        "description": "Retrieve a convo with populated participants, starter, chat and scope. A convo that is still marked active but whose Daily.co room has gone is reaped on read and returned as `completed`. Requires read access to the convo's chat.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "convoId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Convo ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Convo details retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Convo"
                }
              }
            }
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user does not have access to the chat containing this convo"
          },
          "404": {
            "description": "Convo not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Cancel/delete a convo",
        "description": "Marks the convo as `cancelled`, clears the chat's active convo and posts a system message.\nOnly the user who started the convo can delete it.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "convoId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Convo ID"
          }
        ],
        "responses": {
          "204": {
            "description": "Convo cancelled. No response body."
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user is not the convo starter"
          },
          "404": {
            "description": "Convo not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "patch": {
        "summary": "Update a convo's subject and/or notes",
        "description": "Updates `subject` and/or `notes`. Allowed regardless of convo status so notes can be added after the call ends. Only the user who started the convo can update it. At least one field must be provided.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "convoId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Convo ID"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "New subject. An empty string clears it."
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 5000,
                    "description": "New notes. An empty string clears them."
                  }
                },
                "additionalProperties": false,
                "minProperties": 1,
                "example": {
                  "subject": "Design review",
                  "notes": "Agreed to ship the new header on Friday."
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Convo updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "convo": {
                      "$ref": "#/components/schemas/Convo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or no fields to update"
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user is not the convo starter"
          },
          "404": {
            "description": "Convo not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/convos/{convoId}/join": {
      "put": {
        "summary": "Join an active convo",
        "description": "Join an active convo. User must have access to the chat and the convo must be in active status.\nReturns a Daily.co meeting token for the user to join the room.\nUser is added to both activeParticipants and allParticipants arrays.\n\n**Cross-device handover:** If the user is already an active participant (e.g. joining from a second device),\nthe join still succeeds and returns a new meeting token. A `convoHandover` WebSocket notification is sent\nto the user's personal channel (`user/{userId}`) so the old device can close its convo window.\nThe user is not duplicated in activeParticipants.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "convoId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Convo ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully joined the convo (or handed over from another device)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "convo": {
                      "$ref": "#/components/schemas/Convo"
                    },
                    "token": {
                      "type": "string",
                      "description": "Daily.co meeting token to join the room"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The Daily.co room has expired; start a new convo"
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user does not have access to this convo"
          },
          "404": {
            "description": "Convo not found"
          },
          "409": {
            "description": "Convo is not active (`convoNotActive`). `errorData` carries the current `convo` and `chat` so the UI can update."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/convos/{convoId}/rejoin": {
      "put": {
        "summary": "Rejoin a convo (page refresh / reconnect)",
        "description": "Rejoin an active convo after a page refresh or reconnect.\nOnly generates a new Daily.co meeting token if the user is already an active participant.\nNo notifications are sent. This is a silent reconnect.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "convoId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Convo ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully rejoined the convo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "convo": {
                      "$ref": "#/components/schemas/Convo"
                    },
                    "token": {
                      "type": "string",
                      "description": "New Daily.co meeting token"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Convo is not active (`convoNotActive`) or the Daily.co room has expired"
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user does not have access to this convo"
          },
          "404": {
            "description": "Convo not found, or the user is not an active participant"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/convos/{convoId}/leave": {
      "put": {
        "summary": "Leave an active convo",
        "description": "Leave an active convo. User must be an active participant.\nUser is removed from activeParticipants but remains in allParticipants.\nCreates a system message indicating the user left the convo. If the last active participant leaves,\nthe convo is auto-completed and returned with `status: completed`.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "convoId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Convo ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully left the convo. Returns the updated (possibly auto-completed) convo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Convo"
                }
              }
            }
          },
          "400": {
            "description": "Convo is not active (`convoNotActive`)"
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user is not an active participant in this convo"
          },
          "404": {
            "description": "Convo not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/convos/{convoId}/complete": {
      "put": {
        "summary": "Complete a convo",
        "description": "Mark a convo as completed. Only the user who started the convo can complete it.\nCalculates `durationMinutes`, sets `endedAt` and creates a system message indicating the convo was completed.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "convoId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Convo ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Convo completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Convo"
                }
              }
            }
          },
          "400": {
            "description": "Convo is not active (`convoNotActive`)"
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user is not the convo starter"
          },
          "404": {
            "description": "Convo not found"
          },
          "409": {
            "description": "Convo has already been completed or cancelled. `errorData` carries the current `convo` and `chat`."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/convos/scope/{scopeId}": {
      "get": {
        "summary": "Get convos for a project scope (paginated)",
        "description": "Returns convos across all chats in a project, filtered to the requested chat visibilities. The caller must hold the\nchat-read permission for **every** requested visibility (`canGetCreatorChat` / `canGetReviewerChat`).\nActive convos are listed first, then newest first. `search` matches participant names and convo type and is applied\nto the current page after the query.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "scopeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Project ID"
          },
          {
            "name": "visibility",
            "in": "query",
            "required": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "creator",
                  "reviewer"
                ]
              },
              "minItems": 1
            },
            "style": "form",
            "explode": true,
            "description": "Chat visibilities to include (e.g. `visibility[]=creator&visibility[]=reviewer`)"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "completed",
                "cancelled"
              ]
            },
            "description": "Filter convos by status"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive match against participant names and convo type (current page only)"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "cursor",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Results per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only for cursor pagination)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Paginate in reverse order (only for cursor pagination)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include total counts (only for cursor pagination)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include the cursor record in the results (only for cursor pagination)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only for index pagination)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "x-conditionally-required": true
            },
            "description": "Record to start pagination from (only for cursor pagination)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Include the startAt record in the results (only for cursor pagination)"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated convos. Cursor pagination returns a `CursorPaginatedResult`, index pagination a `PaginatedResult`; both carry the convos in `results`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/CursorPaginatedResult"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Convo"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaginatedResult"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Convo"
                              }
                            }
                          }
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error (e.g. missing `visibility`)"
          },
          "403": {
            "description": "Convos disabled (`featureDisabled`) or the user lacks chat access for one of the requested visibilities"
          },
          "404": {
            "description": "Project not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/convos": {
      "get": {
        "summary": "Get all convos for a chat",
        "description": "Retrieve all convos (active, completed, or cancelled) for a specific chat.\nResults are sorted by creation time in descending order (newest first).\nOptionally filter by status. Requires read access to the chat (`canGetChat`) and an active workspace subscription.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Chat ID"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "completed",
                "cancelled"
              ]
            },
            "description": "Filter convos by status"
          }
        ],
        "responses": {
          "200": {
            "description": "Convos retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Convo"
                  }
                }
              }
            }
          },
          "403": {
            "description": "User does not have access to this chat"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/convos": {
      "get": {
        "summary": "Get all convos for a project (Convos drawer)",
        "description": "Retrieve all convos across all chats within a project.\nResults are sorted by creation time in descending order (newest first).\nOptionally filter by visibility (creator/reviewer) and status.\nThis endpoint is used for the \"Convos drawer\" feature to show all project convos in one place.\nRequires `canGetProject` and an active workspace subscription.\n",
        "tags": [
          "Convos"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Project ID"
          },
          {
            "name": "visibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Filter convos by chat visibility (creator or reviewer chats)"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "completed",
                "cancelled"
              ]
            },
            "description": "Filter convos by status"
          }
        ],
        "responses": {
          "200": {
            "description": "Project convos retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Convo"
                  }
                }
              }
            }
          },
          "403": {
            "description": "User does not have access to this project"
          },
          "404": {
            "description": "Project not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/folders/{folderId}": {
      "get": {
        "summary": "Get folder details",
        "description": "Returns a folder by ID. When the folder has an icon, the icon asset is populated on `icon`. Requires `canGetFolder` on the folder and a live subscription on the owning workspace.\n",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "folderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the folder."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved folder details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Folder not found (`folderNotFound`)."
          }
        },
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update folder name or colour",
        "description": "Renames a folder, or updates its colour. The required permission depends on the collection the folder lives in, taken from its `ownerResourceType`: a project-owned folder (the creator and reviewer trees) needs `canUpdateFolder`; one owned by a submission needs `canAddSubmittionItems`; one owned by a public release needs `canUpdatePublicFileSystem`. A creator holds the first but not the latter two, matching the path-scoped routes for those collections. Requires a live subscription on the owning workspace.\n",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "folderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the folder."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "New folder name (HTML-sanitized). Reserved names (`Review`, `Public`, `Submission`) are rejected."
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "New folder colour. Must be one of the platform's approved colors (`colors.approvedColors` from `GET /config`).",
                    "example": "#2962FF"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated folder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or the name is reserved by the system (`reservedFolderName` — `Review`, `Public`, `Submission`)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Folder not found (`folderNotFound`)."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/folders/{folderId}/assets": {
      "get": {
        "summary": "Get assets in a folder (not implemented)",
        "deprecated": true,
        "description": "Deprecated placeholder — this route is not implemented. Every request currently fails with `500 unknownError`; if it were reachable it would respond `501 Not Implemented`. Use `GET /projects/{projectId}/files/{visibility}/{path}` to list a folder's contents.\n",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "folderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the folder."
          }
        ],
        "responses": {
          "500": {
            "description": "Every request currently fails here (`unknownError`); the route is not usable."
          },
          "501": {
            "description": "Not implemented yet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Not implemented yet"
                    }
                  }
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/folders/{folderId}/tag": {
      "put": {
        "summary": "Add a tag to a folder",
        "description": "Adds a project tag to a folder. The tag must belong to the project the folder inherits from, otherwise 404 `tagNotFound`. Requires `canTagFolder` and a live subscription on the owning workspace.\n",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "folderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the folder."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag successfully added to the folder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Folder not found (`folderNotFound`) or tag not found in the folder's project (`tagNotFound`)."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/folders/{folderId}/untag": {
      "put": {
        "summary": "Remove a tag from a folder",
        "description": "Removes a tag from a folder. Requires `canUntagFolder` and a live subscription on the owning workspace.\n",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "folderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the folder."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag successfully removed from the folder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Folder not found (`folderNotFound`)."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/folders/{folderId}/icon": {
      "put": {
        "summary": "Update folder icon",
        "description": "Creates an icon asset for the folder and returns multipart upload URLs for it, mirroring the asset upload flow. Any existing icon asset is marked `pendingDelete`. Requires `canUpdateFolder` and a live subscription on the owning workspace.\n",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "folderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the folder."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "File name of the icon image (HTML-sanitized).",
                    "example": "icon.png"
                  },
                  "checksum": {
                    "type": "string",
                    "description": "Client-computed checksum of the file.",
                    "example": "d9729feb74992cc3482b350163a1a010"
                  },
                  "sizeInMB": {
                    "type": "number",
                    "maximum": 10,
                    "description": "File size in megabytes.",
                    "example": 0.25
                  }
                },
                "required": [
                  "name",
                  "checksum",
                  "sizeInMB"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Icon asset created; upload the file to the returned signed URLs.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AssetAndSignedLink"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "folder": {
                          "$ref": "#/components/schemas/Folder"
                        },
                        "uploadChunkSizeInBytes": {
                          "type": "integer",
                          "format": "int64",
                          "example": 209715200
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Folder not found (`folderNotFound`)."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/invites": {
      "post": {
        "tags": [
          "Invites"
        ],
        "summary": "Invite a user to a resource",
        "description": "Creates an invite and emails the invitee. Requires the right to grant `role` (and every `additionalRoles` entry) on the resource — `canCreateInviteToResource`; otherwise 403. If the invitee already has an account the `inviteCreate` notification is also pushed to their user channel.",
        "operationId": "inviteUserToResource",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "inviteeEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "resourceType": {
                    "type": "string",
                    "enum": [
                      "workspace",
                      "project"
                    ]
                  },
                  "resourceId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "workspaceAdmin",
                      "projectAdmin",
                      "creator",
                      "reviewer",
                      "reviewerAdmin"
                    ]
                  },
                  "additionalRoles": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "workspaceChatMember",
                        "workspaceAdmin"
                      ]
                    },
                    "default": [],
                    "description": "Optional additional workspace-level roles to assign when the invite is accepted."
                  }
                },
                "required": [
                  "inviteeEmail",
                  "resourceType",
                  "resourceId",
                  "role"
                ],
                "additionalProperties": false,
                "example": {
                  "inviteeEmail": "new.member@example.com",
                  "resourceType": "project",
                  "resourceId": "0192b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b",
                  "role": "creator"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invitation created and sent successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invite"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or `inviteToResourceAlreadyExists` — an active invite for this email/resource already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      },
      "get": {
        "tags": [
          "Invites"
        ],
        "summary": "Get user's invites",
        "operationId": "getUsersInvites",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "inviteType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "all",
              "enum": [
                "all",
                "inviter",
                "invitee"
              ]
            },
            "description": "Type of invites to retrieve. Possible values:\n- `all`: Retrieve all invites.\n- `inviter`: Retrieve invites sent by the user.\n- `invitee`: Retrieve invites received by the user.\n"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "active",
                    "canceled",
                    "accepted"
                  ]
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "active",
                      "canceled",
                      "accepted"
                    ]
                  }
                }
              ],
              "default": []
            },
            "description": "Array of invite statuses to filter by. If provided, each element must be one of:\n- `active`\n- `canceled`\n- `accepted`\nSupports duplication (e.g., `['accepted', 'active', 'accepted']`).\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved user's invites. `inviteType=all` returns `{ inviter, invitee }`; `inviter` / `invitee` return a plain array.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "description": "Shape for `inviteType=all`.",
                      "properties": {
                        "inviter": {
                          "$ref": "#/components/schemas/Invites"
                        },
                        "invitee": {
                          "$ref": "#/components/schemas/Invites"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/Invites"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/invites/{inviteId}": {
      "get": {
        "tags": [
          "Invites"
        ],
        "summary": "Get specific invite (public)",
        "description": "Public route — no authentication. Used by the invite landing page before the invitee has an account. Returns the invite with `inviter`, `invitee` and `resource` populated. An unknown id returns an empty body (the record is simply not found; no 404 is raised).",
        "operationId": "getInvite",
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the invite."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved invitation details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invite"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        },
        "x-request-source": "joi"
      },
      "delete": {
        "tags": [
          "Invites"
        ],
        "summary": "Cancel invite",
        "description": "Marks the invite `cancelled`. Requires `canDeleteInvite` on the invite's resource. Returns the cancelled invite record.",
        "operationId": "cancelInvite",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation cancelled successfully. Returns the updated (populated) invite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invite"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/invites/resource/{resourceId}": {
      "get": {
        "summary": "Get invites for a specific resource",
        "tags": [
          "Invites"
        ],
        "parameters": [
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the resource (e.g., workspace, project)."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "active",
                    "canceled",
                    "accepted"
                  ]
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "active",
                      "canceled",
                      "accepted"
                    ]
                  }
                }
              ],
              "default": []
            },
            "description": "Array of invite statuses to filter by. If provided, each element must be one of:\n- `active`\n- `canceled`\n- `accepted`\nSupports duplication (e.g., `['accepted', 'active', 'accepted']`).\n"
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "description": "Requires the `canGetInvite` right on the resource; otherwise 403.",
        "responses": {
          "200": {
            "description": "Successfully retrieved invitations for the resource.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invites"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/invites/accept/{inviteId}": {
      "post": {
        "tags": [
          "Invites"
        ],
        "summary": "Accept an invitation to join a resource",
        "description": "Joins the caller to the invited resource with the invite's role(s). Requires `canAcceptInvite` (the invite must be addressed to the caller's email). Existing members receive a member-join push; a billable role also triggers a subscription-update email to the workspace owner.",
        "operationId": "acceptInvite",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the invite to accept."
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation accepted successfully. Returns invite and new membership details.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invite": {
                      "$ref": "#/components/schemas/Invite"
                    },
                    "membership": {
                      "$ref": "#/components/schemas/Membership"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`inviteExpired`, `inviteCancelled`, `inviteAccepted` or `inviteInvalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "inviteExpired",
                  "code": 400,
                  "message": "Invite expired."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Permission failure, or `inviteRevoked` — the inviter can no longer grant this role."
          },
          "404": {
            "$ref": "#/components/responses/InviteNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "get": {
        "deprecated": true,
        "tags": [
          "Invites"
        ],
        "summary": "Accept an invitation to join a resource (deprecated alias; use POST)",
        "description": "Joins the caller to the invited resource with the invite's role(s). Requires `canAcceptInvite` (the invite must be addressed to the caller's email). Existing members receive a member-join push; a billable role also triggers a subscription-update email to the workspace owner.",
        "operationId": "acceptInvite",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the invite to accept."
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation accepted successfully. Returns invite and new membership details.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invite": {
                      "$ref": "#/components/schemas/Invite"
                    },
                    "membership": {
                      "$ref": "#/components/schemas/Membership"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`inviteExpired`, `inviteCancelled`, `inviteAccepted` or `inviteInvalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "type": "inviteExpired",
                  "code": 400,
                  "message": "Invite expired."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Permission failure, or `inviteRevoked` — the inviter can no longer grant this role."
          },
          "404": {
            "$ref": "#/components/responses/InviteNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/invites/resend/{inviteId}": {
      "post": {
        "tags": [
          "Invites"
        ],
        "summary": "Resend an invite email to the invitee.",
        "description": "Re-sends the invitation email and refreshes the invite's expiry. Requires `canResendInvite` on the invite. Responds with an empty 200 body.",
        "operationId": "resendInvite",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the invite to resend."
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation resent successfully (empty body)."
          },
          "400": {
            "description": "`inviteCancelled`, `inviteAccepted` or `inviteInvalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/InviteNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "get": {
        "deprecated": true,
        "tags": [
          "Invites"
        ],
        "summary": "Resend an invite email to the invitee. (deprecated alias; use POST)",
        "description": "Re-sends the invitation email and refreshes the invite's expiry. Requires `canResendInvite` on the invite. Responds with an empty 200 body.",
        "operationId": "resendInvite",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the invite to resend."
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation resent successfully (empty body)."
          },
          "400": {
            "description": "`inviteCancelled`, `inviteAccepted` or `inviteInvalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/InviteNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/link-preview": {
      "post": {
        "summary": "Fetch link previews for one or more URLs.",
        "tags": [
          "Link Preview"
        ],
        "description": "Accepts an array of URLs and returns Open Graph / meta tag preview data for each.\nEach preview is signed with an HMAC-SHA256 signature that must be included when\nthe preview is submitted with a chat message. Nurama verifies the signature\nwhen the message is created, to prevent spoofed preview data.\n\nDuplicate URLs are collapsed and URLs that fail validation, fetching or\nSSRF checks are silently omitted from `previews`, so the array may be\nshorter than the input. Rate limited to 30 requests per minute per IP.\nRequires authentication.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "urls": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uri",
                      "maxLength": 2048,
                      "description": "Absolute http(s) URL."
                    },
                    "minItems": 1,
                    "maxItems": 5,
                    "description": "Array of URLs to generate previews for.",
                    "example": [
                      "https://github.com",
                      "https://example.com"
                    ]
                  }
                },
                "required": [
                  "urls"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully fetched link previews.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "previews": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LinkPreview"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (invalid URLs, empty array, etc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid authentication token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/memberships": {
      "get": {
        "tags": [
          "Membership"
        ],
        "summary": "Get logged-in user's memberships",
        "description": "Returns every membership record of the caller with `resource` (workspace or project) and public `user` populated. Ordered workspaces first, newest first.",
        "operationId": "getUsersMemberships",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved user's memberships.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Memberships"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/memberships/workspace/{workspaceId}": {
      "get": {
        "tags": [
          "Membership"
        ],
        "summary": "Return membership of a workspace. User must have a workspace admin role to retrieve membership records.",
        "description": "Returns a paginated, per-user membership report covering the workspace and all of its active projects. Requires the `canGetWorkspaceMembers` right. `isBillable=true` restricts to billable roles. Only one of the name sorts (`firstName`, `lastName`, `displayName`) may be used at a time.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the workspace to retrieve membership records for."
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 105,
              "default": null,
              "description": "Optional name search to filter membership records by user names."
            }
          },
          {
            "name": "isBillable",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "description": "Flag to filter memberships based on billable status."
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/SharedIdFirstNameLastName"
            },
            "style": "deepObject",
            "explode": true
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100000,
              "default": 20,
              "description": "Maximum number of membership records to return (default 20, max `limits.membership.maxMembershipRecords`)."
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "default": 1,
              "description": "Page number for pagination (default 1)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved membership records for the specified workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MembershipReportWithTotals"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/memberships/project/{projectId}": {
      "get": {
        "tags": [
          "Membership"
        ],
        "summary": "Return membership of a project. Membership retrieved are determined by a user's role in the project. For example, reviewers can only see reviewers.",
        "description": "Returns a paginated, per-user membership report for the project (same shape as the workspace report). Requires `canGetProjectMembership`; the roles visible depend on the caller's inherited permissions in the project. Only one of the name sorts may be used at a time.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the project to retrieve membership records for."
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 105,
              "default": null,
              "description": "Optional name search to filter membership records by user names."
            }
          },
          {
            "name": "isBillable",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "description": "Flag to filter memberships based on billable status."
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/SharedIdFirstNameLastName"
            },
            "style": "deepObject",
            "explode": true
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100000,
              "default": 20,
              "description": "Maximum number of membership records to return (default 20, max `limits.membership.maxMembershipRecords`)."
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "default": 1,
              "description": "Page number for pagination (default 1)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved membership records for the specified project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MembershipReportWithTotals"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/memberships/{membershipId}": {
      "delete": {
        "tags": [
          "Membership"
        ],
        "summary": "Delete membership record",
        "description": "Removes a member from a resource. Requires `canDeleteMember`. Owner records cannot be deleted. Responds with an empty 200 body.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "membershipId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the membership record to delete."
          }
        ],
        "responses": {
          "200": {
            "description": "Membership deleted successfully (empty body)."
          },
          "400": {
            "description": "`membershipNotFound` or `ownersMayNotBeDelete`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/memberships/leave/{resourceId}": {
      "delete": {
        "tags": [
          "Membership"
        ],
        "summary": "User may delete their own membership record on a resource unless they are a resource owner.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the resource (workspace or project) to leave."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully left the resource (empty body)."
          },
          "400": {
            "description": "Invalid id, `membershipNotFound` (caller has no membership on this resource) or `ownersMayNotBeDelete`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/memberships/add-role": {
      "post": {
        "tags": [
          "Membership"
        ],
        "summary": "Add role to membership record",
        "description": "Requires `canAddMembershipRole` for the role on the membership's resource. Adding an additive role (e.g. `reviewerBoardManager`) auto-pairs its parent role. A billable role triggers a subscription-update email.",
        "operationId": "addRoleToMembership",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "membershipId": {
                    "type": "string",
                    "format": "uuid",
                    "example": "0192b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "workspaceAdmin",
                      "projectAdmin",
                      "creator",
                      "reviewer",
                      "reviewerAdmin",
                      "workspaceChatMember",
                      "reviewerBoardManager"
                    ],
                    "description": "One of the manageable roles listed below. Although the schema does not mark it as required, omitting it fails with `400 roleInvalid`.",
                    "example": "creator"
                  }
                },
                "required": [
                  "membershipId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Role successfully added to membership. Returns the updated membership record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          },
          "400": {
            "description": "`roleInvalid` (unknown or omitted role), `ownershipRolesMayNotBeAdded` or `membershipNotFound`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/memberships/remove-role": {
      "post": {
        "tags": [
          "Membership"
        ],
        "summary": "Remove role from membership record",
        "description": "Requires `canRemoveMembershipRole`. Removing a parent role also strips its additive child roles.",
        "operationId": "removeRoleFromMembership",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "membershipId": {
                    "type": "string",
                    "format": "uuid",
                    "example": "0192b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "workspaceAdmin",
                      "projectAdmin",
                      "creator",
                      "reviewer",
                      "reviewerAdmin",
                      "workspaceChatMember",
                      "reviewerBoardManager"
                    ],
                    "description": "One of the manageable roles listed below. Although the schema does not mark it as required, omitting it fails with `400 roleInvalid`.",
                    "example": "creator"
                  }
                },
                "required": [
                  "membershipId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Role successfully removed from membership. Returns the updated membership record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          },
          "400": {
            "description": "`ownersMayNotBeDelete` (owner roles), `membershipNotFound` or `roleNotFound` (role not on the record).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/memberships/mentionable/project/{projectId}/{visibility}": {
      "get": {
        "tags": [
          "Membership"
        ],
        "summary": "Get mentionable users for a project based on visibility",
        "description": "Requires `canGetProjectMentionableUsers`. Workspace admins of the parent workspace are always included; project roles included depend on `visibility`. Users appearing under several memberships are merged with their roles concatenated.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer",
                "all"
              ]
            },
            "description": "Visibility scope. `creator` returns project creators + admins, `reviewer` returns reviewers + reviewer-admins + admins, and `all` returns the union (used for task chats whose underlying board is visible to both creator and reviewer).\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved mentionable users.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Mentionable"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/memberships/workspace/{workspaceId}/last-seen": {
      "post": {
        "tags": [
          "Membership"
        ],
        "summary": "Get resource-specific last seen timestamps for multiple users in a workspace",
        "description": "Returns last seen data for specified users within a workspace. Requires the `canGetWorkspaceMembers` right. Unknown user ids are silently omitted from `results`; known users with no record get a null `lastSeen`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the workspace to get last seen data for."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid",
                      "example": "507f1f77bcf86cd799439011"
                    },
                    "minItems": 1,
                    "maxItems": 100,
                    "description": "Array of user IDs to retrieve last seen data for (minimum 1, maximum 100)."
                  }
                },
                "required": [
                  "userIds"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved last seen data for users.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "userId": {
                            "type": "string",
                            "description": "The user's ID",
                            "example": "507f1f77bcf86cd799439011"
                          },
                          "resourceId": {
                            "type": "string",
                            "description": "The workspace ID",
                            "example": "507f1f77bcf86cd799439012"
                          },
                          "resourceType": {
                            "type": "string",
                            "enum": [
                              "workspace"
                            ],
                            "description": "The resource type (always 'workspace' for this endpoint)"
                          },
                          "lastSeen": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "description": "ISO 8601 timestamp of when user last accessed the workspace, or null if never accessed",
                            "example": "2025-10-24T10:30:00.000Z"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/memberships/project/{projectId}/last-seen": {
      "post": {
        "tags": [
          "Membership"
        ],
        "summary": "Get resource-specific last seen timestamps for multiple users in a project",
        "description": "Returns last seen data for specified users within a project. Requires `canGetProjectMembership`. Unknown user ids are silently omitted from `results`; known users with no record get a null `lastSeen`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the project to get last seen data for."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid",
                      "example": "507f1f77bcf86cd799439011"
                    },
                    "minItems": 1,
                    "maxItems": 100,
                    "description": "Array of user IDs to retrieve last seen data for (minimum 1, maximum 100)."
                  }
                },
                "required": [
                  "userIds"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved last seen data for users.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "userId": {
                            "type": "string",
                            "description": "The user's ID",
                            "example": "507f1f77bcf86cd799439011"
                          },
                          "resourceId": {
                            "type": "string",
                            "description": "The project ID",
                            "example": "507f1f77bcf86cd799439012"
                          },
                          "resourceType": {
                            "type": "string",
                            "enum": [
                              "project"
                            ],
                            "description": "The resource type (always 'project' for this endpoint)"
                          },
                          "lastSeen": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "description": "ISO 8601 timestamp of when user last accessed the project, or null if never accessed",
                            "example": "2025-10-24T10:30:00.000Z"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/notifications": {
      "post": {
        "summary": "Get paginated notifications for specified channels and types",
        "description": "Retrieves notifications with support for both cursor and index-based pagination",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channels": {
                    "type": "array",
                    "items": {
                      "example": "/project/[projectId]/creator"
                    }
                  },
                  "types": {
                    "$ref": "#/components/schemas/Shared5fee7073"
                  },
                  "paginate": {
                    "type": "string",
                    "default": "index",
                    "enum": [
                      "cursor",
                      "index"
                    ]
                  },
                  "createdAfter": {
                    "type": "integer",
                    "x-conditionally-required": true,
                    "description": "Get notifications created after this timestamp (index pagination only)"
                  },
                  "createdBefore": {
                    "type": "integer",
                    "x-conditionally-required": true,
                    "description": "Get notifications created before this timestamp (index pagination only)"
                  },
                  "page": {
                    "type": "number",
                    "x-conditionally-required": true,
                    "description": "Page number (index pagination only)"
                  },
                  "startAt": {
                    "type": "string",
                    "format": "uuid",
                    "x-conditionally-required": true,
                    "description": "ID of the record to start pagination from (cursor pagination only)"
                  },
                  "includeStartAtRecord": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, includes the startAt record in the results (cursor pagination only)"
                  },
                  "cursor": {
                    "type": "string",
                    "x-conditionally-required": true,
                    "description": "Cursor for pagination (cursor pagination only)"
                  },
                  "paginateReverse": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, paginates in reverse order (cursor pagination only)"
                  },
                  "includeCounts": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, includes count information (cursor pagination only)"
                  },
                  "includeCursorRecord": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, includes the cursor record as first result (cursor pagination only)"
                  },
                  "sort": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "name": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "createdAt": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "type": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      }
                    },
                    "additionalProperties": false
                  },
                  "limit": {
                    "type": "number",
                    "maximum": 1000,
                    "default": 100,
                    "description": "Maximum results per page (up to 1000)."
                  }
                },
                "required": [
                  "channels"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved notifications. Shape depends on `paginate` — `CursorPaginatedNotifications` for `cursor`, `IndexPaginatedNotifications` for `index` (default). Internal push-delivery fields are stripped.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/CursorPaginatedNotifications"
                    },
                    {
                      "$ref": "#/components/schemas/IndexPaginatedNotifications"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/notifications/new": {
      "post": {
        "summary": "Get new notifications with pagination support",
        "description": "Retrieves new notifications with support for both cursor and index-based pagination, optionally updating last seen records. Note that if a cursor is submitted this is essentially the same as calling the /notifications endpoint. Also, if a cursor is submitted you probably want to set the updateLastSeen to false.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "updateLastSeen": {
                    "type": "boolean",
                    "default": true
                  },
                  "channels": {
                    "type": "array",
                    "items": {
                      "example": "/project/[projectId]/creator"
                    }
                  },
                  "types": {
                    "$ref": "#/components/schemas/Shared5fee7073"
                  },
                  "limit": {
                    "type": "number",
                    "maximum": 1000,
                    "default": 100,
                    "description": "Maximum results per page (up to 1000)."
                  },
                  "paginate": {
                    "type": "string",
                    "default": "index",
                    "enum": [
                      "cursor",
                      "index"
                    ]
                  },
                  "cursor": {
                    "type": "string",
                    "x-conditionally-required": true,
                    "description": "Cursor for pagination (cursor pagination only)"
                  },
                  "paginateReverse": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, paginates in reverse order (cursor pagination only)"
                  },
                  "includeCounts": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, includes count information (cursor pagination only)"
                  },
                  "includeCursorRecord": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, includes the cursor record as first result (cursor pagination only)"
                  },
                  "startAt": {
                    "type": "string",
                    "format": "uuid",
                    "x-conditionally-required": true,
                    "description": "ID of the record to start pagination from (cursor pagination only)"
                  },
                  "includeStartAtRecord": {
                    "type": "boolean",
                    "x-conditionally-required": true,
                    "description": "If true, includes the startAt record in the results (cursor pagination only)"
                  },
                  "page": {
                    "type": "number",
                    "x-conditionally-required": true,
                    "description": "Page number (index pagination only)"
                  }
                },
                "required": [
                  "channels"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved new notifications. `paginate=index` (default) returns `IndexPaginatedNewNotifications` (page plus `newNotificationCount`, `createdBefore`, `lastSeen`, `updatedLastSeen`); `paginate=cursor` returns a plain `CursorPaginatedNotifications` (no last-seen metadata; the last-seen record is still updated when `updateLastSeen` is true).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/CursorPaginatedNotifications"
                    },
                    {
                      "$ref": "#/components/schemas/IndexPaginatedNewNotifications"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/notifications/count": {
      "post": {
        "summary": "Get count of new notifications for specified channels and types.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channels": {
                    "type": "array",
                    "items": {
                      "example": "/project/[projectId]/creator"
                    }
                  },
                  "types": {
                    "$ref": "#/components/schemas/Shared5fee7073"
                  }
                },
                "required": [
                  "channels"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved the count of new notifications.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of new records."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/notifications/count/bulk": {
      "post": {
        "summary": "Get bulk count of new notifications for specified channels and types",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channelQueries": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "channels": {
                          "type": "array",
                          "items": {},
                          "description": "Array of channel identifiers"
                        },
                        "types": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "test",
                              "notificationUpdate",
                              "workspaceCreate",
                              "workspaceUpdate",
                              "workspaceDelete",
                              "workspaceLogoUpdate",
                              "projectCreate",
                              "projectUpdate",
                              "projectDelete",
                              "projectLogoUpdate",
                              "projectAssetsPublish",
                              "projectAssetsUnpublish",
                              "projectItemsPublish",
                              "projectItemsUnpublish",
                              "projectGroupItemPublish",
                              "assetNameChange",
                              "assetPublish",
                              "assetUnpublish",
                              "assetTag",
                              "assetUntag",
                              "publicAssetLinkCreate",
                              "assetDelete",
                              "assetStatusUpdate",
                              "assetPostProcessUpdate",
                              "assetFileUpdate",
                              "assetGroupUploadComplete",
                              "uploadPushSummary",
                              "publishPushSummary",
                              "submissionPushSummary",
                              "memberJoinPush",
                              "chatTopicChatCreate",
                              "chatMemberChatCreate",
                              "chatUpdateSubject",
                              "chatMemberUpdate",
                              "chatDelete",
                              "chatMemberDelete",
                              "chatMemberArchive",
                              "chatMemberUnarchive",
                              "chatCreateMessage",
                              "chatReviseMessage",
                              "chatRefreshMessage",
                              "chatMention",
                              "chatDeleteMessage",
                              "chatRemoveAttachment",
                              "chatHighlightMessage",
                              "chatFollow",
                              "chatUnfollow",
                              "folderCreate",
                              "folderUpdate",
                              "folderDelete",
                              "folderPublish",
                              "folderTag",
                              "folderUntag",
                              "inviteCreate",
                              "inviteCancel",
                              "inviteAccept",
                              "membershipDelete",
                              "membershipAddRole",
                              "membershipRemoveRole",
                              "membershipLeaveResource",
                              "workspaceStorageLimitWarning",
                              "userEmailVerify",
                              "userSelfUpdate",
                              "userPublicUpdate",
                              "userDeleted",
                              "userAvatarUpdate",
                              "botCreate",
                              "webhookTest",
                              "logoUpdate",
                              "iconUpdate",
                              "subscriptionCreate",
                              "subscriptionUpdate",
                              "taskAcknowledged",
                              "taskStatusUpdate",
                              "taskCreate",
                              "taskFollow",
                              "taskUnfollow",
                              "notificationUpdateLastSeen",
                              "submissionCreate",
                              "submissionUpdate",
                              "submissionTag",
                              "submissionUntag",
                              "aiChatTopicCreate",
                              "aiChatTopicUpdate",
                              "aiChatMessageCreate",
                              "tagCreate",
                              "tagUpdate",
                              "tagDelete",
                              "fileSystemCreate",
                              "fileSystemMove",
                              "fileSystemCopy",
                              "fileSystemDelete",
                              "fileSystemPublish",
                              "fileSystemUnpublish",
                              "publicFileSystemCreate",
                              "publicFileSystemUpdate",
                              "publicFileSystemDelete",
                              "settingsUpdate",
                              "convoStart",
                              "convoJoin",
                              "convoLeave",
                              "convoComplete",
                              "convoUpdate",
                              "convoDelete",
                              "convoParticipantJoined",
                              "convoParticipantLeft",
                              "convoHandover",
                              "boardCreate",
                              "boardUpdate",
                              "boardDelete",
                              "boardTaskCreate",
                              "boardTaskMove",
                              "boardTaskAssign",
                              "boardColumnAdd",
                              "boardColumnUpdate",
                              "boardColumnDelete",
                              "boardColumnReorder",
                              "boardTaskAdd",
                              "boardTaskRemove",
                              "boardTaskUpdate",
                              "boardTaskLink",
                              "boardTaskUnlink",
                              "boardTaskRelationAdd",
                              "boardTaskRelationRemove",
                              "boardFollow",
                              "boardUnfollow"
                            ]
                          },
                          "uniqueItems": true,
                          "description": "Optional array of notification types (notification `type` names, e.g. `chatMessageCreate`; see the WebSocket event reference)."
                        }
                      },
                      "required": [
                        "channels"
                      ],
                      "additionalProperties": false
                    },
                    "minItems": 1
                  }
                },
                "required": [
                  "channelQueries"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved bulk counts of new notifications. One entry per `channelQueries` item, in request order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "channels": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "description": "Array of channel identifiers"
                      },
                      "types": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "description": "Array of notification types as sent in the request (empty array when omitted)"
                      },
                      "count": {
                        "type": "integer",
                        "description": "Number of new notifications for the specified channels and types"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/notifications/last-seen": {
      "post": {
        "summary": "Get user's last seen records for specified channels and types.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channels": {
                    "type": "array",
                    "items": {
                      "example": "/project/[projectId]/creator"
                    }
                  },
                  "types": {
                    "$ref": "#/components/schemas/Shared5fee7073"
                  }
                },
                "required": [
                  "channels"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved the last seen record matching exactly these channels (and types). Body is `null` when no record exists yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LastSeen"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update user's last seen records for specified channels and types.",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channels": {
                    "type": "array",
                    "items": {
                      "example": "/project/[projectId]/creator"
                    }
                  },
                  "types": {
                    "$ref": "#/components/schemas/Shared5fee7073"
                  }
                },
                "required": [
                  "channels"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated last seen records.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LastSeen"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/projects": {
      "post": {
        "summary": "Create a new project and topic chats with for both reviewers and creators.",
        "description": "Requires `canCreateProject` on the target workspace and an active workspace subscription. Returns `maxProjectsPerWorkspace` (400) when the workspace has reached its plan's project cap and `projectNameTaken` (400) when a project with the same slug already exists in the workspace.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Project name. Also used to derive the project slug.",
                    "example": "Spring Campaign"
                  },
                  "workspaceId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workspace the project is created in. The caller needs `canCreateProject` on it."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Project created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, `projectNameTaken` or `maxProjectsPerWorkspace`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Get projects for the authenticated user",
        "description": "Returns every active project the caller is a member of, with the caller's role attached to each project.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort options (object-style query, e.g. `sort[name]=1`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Returns user's projects and their role in each project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Projects"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}": {
      "post": {
        "summary": "Create assets and signed upload links for a project.",
        "description": "Creates assets in the project's creator tree and returns signed upload links. Requires `canCreateAsset`. When a submitted file's checksum matches an asset already in the project, no bytes are transferred: the platform adds another FileSystem reference to the existing asset and reports `status: success` with `referencesExistingAsset: true` and no signed URLs.\nIn that case the response's `name` is the EXISTING asset's name, because that is what the new reference carries — `FileSystem.resourceName` is a denormalized copy of `Asset.name`, so the submitted name never lands. The submitted name is returned as `originalName`. Correlate results with requests positionally or by your own upload id, not by `name`.\nReturns `uploadRequestExceedsSubscription` (400) when the request would exceed the workspace's subscribed storage.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer",
                          "maximum": 10,
                          "description": "This ID is for the requesters use only. It may be any number. It is uses to match request items with response items."
                        },
                        "name": {
                          "type": "string",
                          "pattern": "^(?!.*\\.(exe|bat|com|msi|vbs|ps1|app|command|tool|sh|bin|run|jar|py|pl|rb)$).*",
                          "description": "Name of the file (must include a valid extension)."
                        },
                        "checksum": {
                          "type": "string",
                          "pattern": "^[a-f0-9]{32,64}$",
                          "description": "MD5 or SHA-256 hash of the file content"
                        },
                        "sizeInMB": {
                          "type": "number",
                          "minimum": 0,
                          "x-exclusiveMinimum": true,
                          "description": "File size in MB. This is used only to determine how many signed links to generate for the file and can be rounded to 2 decimals."
                        },
                        "basePath": {
                          "type": "string",
                          "description": "The base path of the file. This is used to create the initial file system path for the file. If undefined the project's root path is used."
                        }
                      },
                      "required": [
                        "id",
                        "name",
                        "checksum",
                        "sizeInMB"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "destinationPath": {
                    "type": "string",
                    "description": "Creator-tree folder path the files should be added to, relative to the project root (a path already prefixed with `project/{projectId}/creator` is accepted as-is). Omit to upload to the project root.\n"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assets and upload links created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetAndSignedLinks"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or `uploadRequestExceedsSubscription`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Get project details",
        "description": "Requires `canGetProject`. The caller's roles on the project are attached to the response.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved project details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update project details",
        "description": "Requires `canUpdateProject`. A `projectUpdate` notification is emitted only when the name actually changes.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 350
                  },
                  "updateSlug": {
                    "type": "boolean",
                    "default": false,
                    "description": "Determines if the slug will be regenerated or not. Meaning you could change the name but keep the old slug as it may be used in static links."
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "Hex color code for the project. Must be one of the approved colours."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated project details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Mark a project for deletion.",
        "description": "Requires `canDeleteProject`. Holders are identical to `canUpdateProject` — which is what this route used to check — so no caller's access changed; the right that names the operation is now the one that gates it, so revoking it from a role actually blocks deletion.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Project successfully marked for deletion. Empty body."
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/search": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Full-text search across project content.",
        "description": "Searches across assets, chat messages, and tasks within a project using full-text search. The query uses web-search syntax: unquoted words must all match, `\"quoted text\"` matches a phrase, `OR` matches either side and a leading `-` excludes a word. Results are ranked by relevance and respect the caller's visibility (creator / reviewer) permissions. Each hit includes a populated `record` shaped per the content type's native list view. Requires `canGetProject`.\n",
        "operationId": "searchProject",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "Search term, 1–200 characters (whitespace trimmed). Supports web-search syntax (`\"quoted phrase\"`, `OR`, leading `-` to exclude a word)."
          },
          {
            "name": "contentTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "asset",
                      "chatMessage",
                      "task"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "asset",
                    "chatMessage",
                    "task"
                  ]
                }
              ]
            },
            "description": "Restrict to one or more content types. Omit to include all."
          },
          {
            "name": "dateFrom",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "ISO 8601 date. Only return results created on/after this date."
          },
          {
            "name": "dateTo",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "ISO 8601 date. Only return results created on/before this date."
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Restrict results to content authored by this user."
          },
          {
            "name": "sortBy",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "relevance",
              "enum": [
                "relevance",
                "dateAsc",
                "dateDesc"
              ]
            },
            "description": "Sort order."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated, populated search results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResults"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/logo": {
      "post": {
        "deprecated": true,
        "tags": [
          "Projects"
        ],
        "summary": "Create or update project logo. (deprecated alias; use PUT)",
        "description": "Creates the logo asset and returns its signed upload link(s). POST and PUT run the same operation. Requires `canUpdateProject`. Returns `uploadRequestExceedsSubscription` (400) when the file would exceed the workspace's subscribed storage.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Logo asset and upload links created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateLogoResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or `uploadRequestExceedsSubscription`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "tags": [
          "Projects"
        ],
        "summary": "Create or update project logo.",
        "description": "Identical to `POST /projects/{projectId}/logo`. Requires `canUpdateProject`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Logo asset and upload links created/updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateLogoResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or `uploadRequestExceedsSubscription`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/chat/{visibility}": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get project topic chat (creator or reviewer).",
        "description": "Returns the project's topic chat for the given visibility with its most recent messages and replies. Requires `canGetCreatorChat` for `creator` and `canGetReviewerChat` for `reviewer`.\n",
        "operationId": "getProjectChat",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          },
          {
            "name": "updatedBefore",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Millisecond timestamp. When set, only messages updated before this time are returned (legacy filter; forces the creator chat).\n"
          },
          {
            "name": "messages",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of recent messages to retrieve"
          },
          {
            "name": "replies",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of recent replies to retrieve per message."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting parameter for chat messages and replies"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved project chat messages and details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Chat"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/feed/{visibility}": {
      "get": {
        "summary": "Get project feed with assets and chats",
        "description": "Retrieves a paginated feed of assets and their associated chats for a project. Requires `canGetCreatorAssets` for `creator` and `canGetReviewerAssets` for `reviewer`. Index pagination is the default; pass `paginate=cursor` for cursor pagination. Cursor-only parameters (`cursor`, `paginateReverse`, `includeCounts`, `includeCursorRecord`, `startAt`, `includeStartAtRecord`) are rejected with index pagination, and `page`, `createdBefore`, `createdAfter` are rejected with cursor pagination.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "file",
                      "3d",
                      "document"
                    ]
                  },
                  "uniqueItems": true
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "file",
                    "3d",
                    "document"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter assets by media type (single value or array)."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "lastMessageAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "publishedOn": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for the feed"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 20,
              "default": 20
            },
            "description": "Number of results per page"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100,
              "default": null
            },
            "description": "Search assets by name"
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the upper bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "createdAfter",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the lower bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "chatMessageSort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat messages"
          },
          {
            "name": "chatReplySort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat replies"
          },
          {
            "name": "chatMessageLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded chat messages to return per chat"
          },
          {
            "name": "chatReplyLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded replies to return per message"
          },
          {
            "name": "hideIfNoChatMessages",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, hides assets with no chat messages"
          },
          {
            "name": "followedChatsOnly",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, filters feed to show only chats the user is following"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved project feed. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Feed"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFeed"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public-audit": {
      "get": {
        "summary": "Get the public audit report for a project",
        "description": "Returns a paginated list of project assets that are or have been publicly exposed —\neither via a `PublicAssetLink` (direct download link) or via membership in a `Public`\nfile system. Requires `canGetPublicAudit` (project/workspace owners and admins).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "currentlyPublic",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "When `true`, returns only assets currently public — those with an active\n`PublicAssetLink` or membership in an active, non-expired public file system.\nWhen `false` (default), returns all assets that have ever been public.\n"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number for index pagination."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Number of results per page."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved public audit report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAuditAssets"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission": {
      "post": {
        "summary": "Create a new submission for a project",
        "description": "Note, submissions are a Chat model. Items are taken from the project's reviewer tree (`itemPaths` are relative to `project/{projectId}/reviewer`; already-prefixed paths are accepted as-is). Requires `canCreateSubmission`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "default": [],
                    "description": "Reviewer-tree item paths to include in the submission."
                  },
                  "subject": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Subject of the submission"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 5000,
                    "description": "Description of the submission"
                  },
                  "version": {
                    "type": "string",
                    "maxLength": 10,
                    "description": "Version of the submission"
                  },
                  "releaseImmediately": {
                    "type": "boolean",
                    "default": true,
                    "description": "When false, the submission is staged as `unreleased` — hidden from reviewers and firing none of the \"new submission\" side effects (emails, notifications, system messages) until it is released via the `/release` endpoint. Defaults to true (goes live immediately)."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created chat submission.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Get submissions for a project",
        "description": "Requires `canGetSubmission`. Callers holding `canCreateSubmission` also see staged `unreleased` submissions; everyone else sees only `active` ones. Index pagination is the default; pass `paginate=cursor` for cursor pagination (cursor-only parameters are rejected with index pagination, and `page`, `createdBefore`, `createdAfter` are rejected with cursor pagination).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "subject": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "createdAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Case-insensitive substring match against submission subject."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of results per page"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the upper bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "createdAfter",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the lower bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "chatMessageSort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat messages"
          },
          {
            "name": "chatReplySort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat replies"
          },
          {
            "name": "chatMessageLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded chat messages to return per chat"
          },
          {
            "name": "chatReplyLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded replies to return per message"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved project submissions. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Submissions"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedSubmissions"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}": {
      "get": {
        "summary": "Get a specific submission for a project with recent messages.",
        "description": "Note, submissions are a Chat model. Requires `canGetSubmission`. The submission must belong to `projectId`; a foreign or unknown id returns `submissionNotFound` (404).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          },
          {
            "name": "chatMessageSort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat messages"
          },
          {
            "name": "chatReplySort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat replies"
          },
          {
            "name": "chatMessageLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded chat messages to return per chat"
          },
          {
            "name": "chatReplyLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded replies to return per message"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved chat submission.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ChatSubmission"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "queryAt": {
                          "type": "integer",
                          "format": "int64",
                          "description": "Millisecond timestamp of when the query ran."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/SubmissionNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update a submission",
        "description": "Requires `canUpdateSubmission`. The submission must belong to `projectId`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Updated subject of the submission"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 5000,
                    "description": "Updated description of the submission"
                  },
                  "version": {
                    "type": "string",
                    "maxLength": 10,
                    "description": "Updated version of the submission"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submission updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/SubmissionNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/tag": {
      "put": {
        "summary": "Add a tag to a submission",
        "description": "Requires `canTagSubmission`. The submission must belong to `projectId`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The ID of the tag to add"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag added successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission or tag not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      },
      "post": {
        "deprecated": true,
        "summary": "Add a tag to a submission (deprecated alias; use PUT)",
        "description": "Requires `canTagSubmission`. The submission must belong to `projectId`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The ID of the tag to add"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag added successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission or tag not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/untag": {
      "put": {
        "summary": "Remove a tag from a submission",
        "description": "Requires `canUntagSubmission`. The submission must belong to `projectId`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The ID of the tag to remove"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag removed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission or tag not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      },
      "post": {
        "deprecated": true,
        "summary": "Remove a tag from a submission (deprecated alias; use PUT)",
        "description": "Requires `canUntagSubmission`. The submission must belong to `projectId`.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The ID of the tag to remove"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag removed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission or tag not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/release": {
      "post": {
        "summary": "Release a staged (unreleased) submission",
        "description": "Flips a submission's status from `unreleased` to `active`, making it visible to reviewers and firing the deferred \"new submission\" side effects (project-member email, notification, push summary, and system messages in the creator + reviewer project chats). Requires `canUpdateSubmission`. Returns `submissionNotUnreleased` (400) if the submission isn't in the `unreleased` state.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The released chat submission.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (e.g. `submissionNotUnreleased`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/SubmissionNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/release-update": {
      "post": {
        "summary": "Re-release an already-released submission's side effects",
        "description": "The \"Release Update\" action for a live (`active`) submission. Re-fires the reviewer-facing side effects — the submission-update email template, a `submissionUpdate` notification, and system messages in the creator + reviewer project chats — to announce that a released submission has changed. Does NOT change the submission's status. Requires `canUpdateSubmission`. Returns `submissionNotFound` if no active submission matches.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The submission (status unchanged).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/SubmissionNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/files": {
      "get": {
        "summary": "Get file system items at the root of a submission",
        "description": "Lists the items directly under `submission/{submissionId}`. Requires `canGetSubmission`; the submission must belong to `projectId`. Each item carries its populated `resource` and, for assets, the reviewer chat. Index pagination is the default; pass `paginate=cursor` for cursor pagination (cursor-only parameters are rejected with index pagination and `page` is rejected with cursor pagination). `resourceIds` and `resourceSlugs` may not both be set (`invalidItemSearchParams`, 400).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "If true, includes items in every sub-folder beneath the path instead of only direct children."
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource IDs. May not be combined with `resourceSlugs`."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource slugs. May not be combined with `resourceIds`."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs"
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the resource creator's user ID"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types (single value or array)"
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Partial, case-insensitive match on the resource name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria. Keys are mapped onto the FileSystem entry's resource fields (`name` → `resourceName`, `createdAt` → `resourceCreatedAt`, ...)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of results per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved submission files (includes `queryAt`).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/SubmissionNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/files/{path}": {
      "get": {
        "summary": "Get file system items at a path within a submission",
        "description": "Same as the root listing, scoped to a folder path inside the submission. Path segments may be folder slugs or IDs (IDs are resolved to slugs; an unknown segment returns `fileSystemPathNotFound`, 404). Requires `canGetSubmission`; the submission must belong to `projectId`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Folder path within the tree. May contain several `/`-separated segments; each segment is a folder slug or ID.",
            "example": "Renders/2024"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "If true, includes items in every sub-folder beneath the path instead of only direct children."
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource IDs. May not be combined with `resourceSlugs`."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource slugs. May not be combined with `resourceIds`."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs"
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the resource creator's user ID"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types (single value or array)"
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Partial, case-insensitive match on the resource name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria. Keys are mapped onto the FileSystem entry's resource fields (`name` → `resourceName`, `createdAt` → `resourceCreatedAt`, ...)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of results per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved submission files at path (includes `queryAt`).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission not found, or a path segment does not exist (`fileSystemPathNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/files/create-folder": {
      "post": {
        "summary": "Create a new folder within a submission",
        "description": "Requires `canCreateSubmissionFolder`; the submission must belong to `projectId`. Reserved names return `reservedFolderName` (400), a `basePath` inside a virtual folder returns `protectedBasePath` (400), and a `basePath` that does not exist in the submission returns `basePathNotFound` (404). Recomputes the submission's inventory.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedNameColorBasePath"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Folder created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, `reservedFolderName` or `protectedBasePath`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Submission not found or `basePathNotFound`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/files/move": {
      "put": {
        "summary": "Move items within a submission",
        "description": "Paths are relative to `submission/{submissionId}`. Items already in the destination folder are skipped (`count` may be 0). Requires `canMoveSubmissionItems`; the submission must belong to `projectId`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to move",
                    "example": [
                      "Renders/final.png",
                      "Renders/alt"
                    ]
                  },
                  "destinationPath": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Destination folder path for the items",
                    "example": "Approved"
                  }
                },
                "required": [
                  "itemPaths",
                  "destinationPath"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items moved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission not found, or a source/destination path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/files/copy": {
      "put": {
        "summary": "Copy items within a submission",
        "description": "Paths are relative to `submission/{submissionId}`. Requires `canCopySubmissionItems`; the submission must belong to `projectId`. Recomputes the submission's inventory.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to copy",
                    "example": [
                      "Renders/final.png"
                    ]
                  },
                  "destinationPath": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Destination folder path for the items",
                    "example": "Approved"
                  }
                },
                "required": [
                  "itemPaths",
                  "destinationPath"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items copied successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission not found, or a source/destination path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/files/delete": {
      "put": {
        "summary": "Delete items within a submission",
        "description": "Paths are relative to `submission/{submissionId}`. Requires `canDeleteSubmissionItems`; the submission must belong to `projectId`. Recomputes the submission's inventory.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to delete",
                    "example": [
                      "Renders/old.png"
                    ]
                  }
                },
                "required": [
                  "itemPaths"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission not found, or an item path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/submission/{submissionId}/add": {
      "post": {
        "summary": "Add reviewer-tree items to an existing submission",
        "description": "Copies items from the project's reviewer tree into the submission. `itemPaths` are relative to `project/{projectId}/reviewer` and `destinationPath` is relative to `submission/{submissionId}` (omit it to add at the submission root; a destination folder that does not exist returns 404). Marks the copied assets as belonging to the submission and recomputes its inventory. Requires `canAddSubmittionItems`; the submission must belong to `projectId`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Reviewer-tree item paths to add.",
                    "example": [
                      "Renders/final.png",
                      "Renders/alt"
                    ]
                  },
                  "destinationPath": {
                    "type": "string",
                    "description": "Folder inside the submission to add the items to. Defaults to the submission root.",
                    "example": "Round 2"
                  }
                },
                "required": [
                  "itemPaths"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items added successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Submission not found, or a source/destination path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "submissionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the submission. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/assets/{visibility}": {
      "get": {
        "summary": "Get assets for a project with specified visibility",
        "description": "Retrieves assets for a project filtered by visibility level, optionally with their chats, with support for both cursor and index-based pagination. Requires `canGetCreatorAssets` for `creator` and `canGetReviewerAssets` for `reviewer`. Cursor-only parameters are rejected with index pagination, and `page`, `createdBefore`, `createdAfter` are rejected with cursor pagination.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100,
              "default": null
            },
            "description": "Search assets by name"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "file",
                      "3d",
                      "document"
                    ]
                  },
                  "uniqueItems": true
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "file",
                    "3d",
                    "document"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter assets by media type (single value or array)."
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the upper bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "createdAfter",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Timestamp or ID indicating the lower bound of result creation time. You may preface an ID with a '+' to make the results inclusive of that result Meaning <= instead of just <."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "publishedOn": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for assets"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 20,
              "default": 20
            },
            "description": "Number of results per page"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeChats",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true
            },
            "description": "Include associated chats in the response"
          },
          {
            "name": "chatMessageSort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat messages"
          },
          {
            "name": "chatReplySort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for embedded chat replies"
          },
          {
            "name": "chatMessageLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded chat messages to return per chat"
          },
          {
            "name": "chatReplyLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Maximum number of embedded replies to return per message"
          },
          {
            "name": "hideIfNoChatMessages",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, hides assets with no chat messages"
          },
          {
            "name": "folderId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "default": null,
              "nullable": true
            },
            "description": "Restrict to assets in this folder."
          },
          {
            "name": "ignoreFolder",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, ignores folder placement when listing assets."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved assets. Shape depends on `paginate`; `chats.{visibility}` is populated only when `includeChats` is true.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Feed"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFeed"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/files/{visibility}/create-folder": {
      "post": {
        "summary": "Create a new folder in a project",
        "description": "Creates a folder in the creator or reviewer tree. The required permission depends on `visibility`, as it does for move and copy: `creator` needs `canCreateFolder`; `reviewer` needs `canCreateReviewerFolder`, which only the workspace and project admin tiers hold. A creator can publish into the reviewer tree but not create folders there directly, matching the fact that they hold no reviewer move or copy right. Reserved names (`Review`, `Public`, `Submission`) are rejected (`reservedFolderName`, 400), as are base paths inside a virtual folder (`protectedBasePath`, 400). A `basePath` that does not exist returns `basePathNotFound` (404).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedNameColorBasePath"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Folder created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, `reservedFolderName` or `protectedBasePath`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Project not found or `basePathNotFound`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/files/{visibility}/move": {
      "put": {
        "summary": "Move items to a new path within a project",
        "description": "Paths are relative to `project/{projectId}/{visibility}`. Requires `canMoveCreatorItems` for `creator` and `canMoveReviewerItems` for `reviewer`. Items already in the destination folder are skipped (`count` may be 0).\n\nA destination inside a virtual folder is a copy, not a move, and costs the right of the matching add route: `Public/{token}/...` requires `canAddPubicFileSystemItems`, `Submission/{id}/...` requires `canAddSubmittionItems`, and dropping onto `Review/...` publishes the items (requires `canPublishAsset`). Those responses carry `virtualDestination: true` (and `published: true` plus `errors` for `Review/`).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to move",
                    "example": [
                      "Renders/final.png",
                      "Renders/alt"
                    ]
                  },
                  "destinationPath": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Destination folder path for the items",
                    "example": "Approved"
                  }
                },
                "required": [
                  "itemPaths",
                  "destinationPath"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items moved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Project not found, or a source/destination path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/files/{visibility}/copy": {
      "put": {
        "summary": "Copy items to a new path within a project",
        "description": "Paths are relative to `project/{projectId}/{visibility}`. Requires `canCopyCreatorItems` for `creator` and `canCopyReviewerItems` for `reviewer`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to copy",
                    "example": [
                      "Renders/final.png"
                    ]
                  },
                  "destinationPath": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Destination folder path for the items",
                    "example": "Approved"
                  }
                },
                "required": [
                  "itemPaths",
                  "destinationPath"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items copied successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Project not found, or a source/destination path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/files/{visibility}/delete-preview": {
      "post": {
        "summary": "Preview what deleting items at these paths would reach",
        "description": "Answers what a delete would remove beyond the rows named, without deleting anything. Takes the same body the delete takes and resolves it through the same cascade, so the preview cannot disagree with the outcome.\n\nOnly SECONDARY references come back — the reviewer, submission and public-release copies that would go with the selection — not the rows the caller listed.\n\nCosts the same right as the delete itself (`canDeleteCreatorItems` / `canDeleteReviewerItems`). A cheaper preview would hand out the labels of submissions and public releases the caller cannot otherwise see.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to delete",
                    "example": [
                      "Renders/old.png"
                    ]
                  }
                },
                "required": [
                  "itemPaths"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The impact of the proposed delete",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteImpact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Project not found, or an item path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/files/{visibility}/delete": {
      "put": {
        "summary": "Delete items at specified paths within a project",
        "description": "Paths are relative to `project/{projectId}/{visibility}`. Requires `canDeleteCreatorItems` for `creator` and `canDeleteReviewerItems` for `reviewer`. Use `delete-preview` first to learn which secondary references the cascade will remove.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to delete",
                    "example": [
                      "Renders/old.png"
                    ]
                  }
                },
                "required": [
                  "itemPaths"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Project not found, or an item path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "deprecated": true,
        "summary": "Delete items at specified paths within a project (deprecated alias; use PUT)",
        "description": "Paths are relative to `project/{projectId}/{visibility}`. Requires `canDeleteCreatorItems` for `creator` and `canDeleteReviewerItems` for `reviewer`. Use `delete-preview` first to learn which secondary references the cascade will remove.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to delete",
                    "example": [
                      "Renders/old.png"
                    ]
                  }
                },
                "required": [
                  "itemPaths"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Project not found, or an item path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/files/{visibility}": {
      "get": {
        "summary": "Get items at the root path of a project",
        "description": "Lists the items directly under `project/{projectId}/{visibility}`. Requires `canGetCreatorItems` for `creator` and `canGetReviewerItems` for `reviewer`. On the first page of an unfiltered listing the synthesized virtual roots (`Review`, `Public`, `Submission`) the caller may read are prepended to `results`. Index pagination is the default; pass `paginate=cursor` for cursor pagination (cursor-only parameters are rejected with index pagination and `page` is rejected with cursor pagination). `resourceIds` and `resourceSlugs` may not both be set (`invalidItemSearchParams`, 400).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "If true, includes items in every sub-folder beneath the path instead of only direct children."
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource IDs. May not be combined with `resourceSlugs`."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource slugs. May not be combined with `resourceIds`."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs"
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the resource creator's user ID"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types (single value or array)"
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Partial, case-insensitive match on the resource name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria. Keys are mapped onto the FileSystem entry's resource fields (`name` → `resourceName`, `createdAt` → `resourceCreatedAt`, ...)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of results per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved items at path. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "post": {
        "summary": "Get items at the root path of a project (filters in the body)",
        "description": "Identical to the GET listing, but the query options are sent as a JSON body instead of query-string parameters — useful when the filter set (for example a long `resourceIds` list) would not fit in a URL. Same permissions and response shapes as the GET.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedPaginateRecursiveSearchResourceIds"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved items at path. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/files/{visibility}/{path}": {
      "get": {
        "summary": "Get items at a specific path within a project",
        "description": "Same as the root listing, scoped to a folder path. Path segments may be folder slugs or IDs (IDs are resolved to slugs; an unknown segment returns `fileSystemPathNotFound`, 404). Virtual paths are supported: `Public` and `Submission` list one synthesized sub-folder per release / submission the caller may read (`canGetPublicFileSystem` / `canGetSubmission`), and `Public/{token}/...`, `Submission/{id}/...` and `Review/...` are translated to the underlying tree. Requires `canGetCreatorItems` for `creator` and `canGetReviewerItems` for `reviewer`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Folder path within the tree. May contain several `/`-separated segments; each segment is a folder slug or ID.",
            "example": "Renders/2024"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "If true, includes items in every sub-folder beneath the path instead of only direct children."
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource IDs. May not be combined with `resourceSlugs`."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource slugs. May not be combined with `resourceIds`."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs"
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the resource creator's user ID"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types (single value or array)"
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Partial, case-insensitive match on the resource name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria. Keys are mapped onto the FileSystem entry's resource fields (`name` → `resourceName`, `createdAt` → `resourceCreatedAt`, ...)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of results per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved items at path. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Project not found, or a path segment does not exist (`fileSystemPathNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      },
      "post": {
        "summary": "Get items at a specific path within a project (filters in the body)",
        "description": "Identical to the GET listing at a path, but the query options are sent as a JSON body instead of query-string parameters. Same permissions and response shapes as the GET.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedPaginateRecursiveSearchResourceIds"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully retrieved items at path. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Project not found, or a path segment does not exist (`fileSystemPathNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "visibility",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Which tree to operate on — the creator tree or the reviewer tree."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Folder path within the tree. May contain several `/`-separated segments; each segment is a folder slug or ID.",
            "example": "Renders/2024"
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/publish": {
      "post": {
        "summary": "Publish assets",
        "description": "Bulk publish an array of file-system items (assets or folders) into the project's reviewer tree. The caller must hold `canPublishItem` on every listed resource; otherwise 403 with the offending ids in `errorData`. Fires per-item and project-level publish notifications, system messages in the creator and reviewer chats and, when `sendEmailNotification` is true, the published-items email.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "resourceIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Array of resource IDs (assets or folders) to publish"
                  },
                  "sendEmailNotification": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether to send the published-items email"
                  },
                  "basePath": {
                    "type": "string",
                    "description": "Reviewer-tree folder path to publish beneath. Empty string or omitted publishes at the reviewer root."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Publish result — the published items, the total count (including recursive folder contents) and per-item errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishItemsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "The caller lacks `canPublishItem` on one or more of the listed resources (ids in `errorData`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/unpublish": {
      "post": {
        "summary": "Unpublish assets",
        "description": "Bulk unpublish an array of assets, removing their reviewer-tree references. Only assets can be unpublished (a folder is removed from the reviewer surface by deleting it there). The caller must hold `canUnpublishItem` on every listed resource; otherwise 403 with the offending ids in `errorData`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "resourceIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Array of asset IDs to unpublish"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unpublish result — the unpublished assets, the total count, the removed reviewer paths and per-item errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnpublishItemsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "The caller lacks `canUnpublishItem` on one or more of the listed resources (ids in `errorData`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public": {
      "post": {
        "summary": "Create a public file system",
        "description": "Creates a public file system (a \"release\") with a secure token for sharing project assets publicly without authentication. `itemPaths` are relative to `project/{projectId}/creator`. Requires `canCreatePublicFileSystem`.\n\nGated by the `publicSharing` workspace capability — granted by every paid base plan and intentionally withheld from the Demo plan. Requests from workspaces without the capability return 403 `capabilityNotAvailable` with `errorData.capability = \"publicSharing\"`; clients can use this to prompt the user to upgrade their plan.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 0,
                    "default": [],
                    "description": "Creator-tree item paths to include in the public file system",
                    "example": [
                      "Renders/final.png"
                    ]
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Title for the public file system",
                    "example": "Client review – round 1"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Optional description for the public file system (may be empty)"
                  },
                  "validity": {
                    "type": "number",
                    "minimum": 0,
                    "x-exclusiveMinimum": true,
                    "maximum": 2592000000,
                    "nullable": true,
                    "description": "Validity period in milliseconds from now (max 30 days). Omit or pass `null` for a release that never expires.\n"
                  },
                  "hideCreators": {
                    "type": "boolean",
                    "description": "When `true`, the external public API represses authorship for this release: per-item asset/folder creators AND the release's own \"Shared by\" creator are omitted from the `/public/{token}*` responses. The stored `creatorId` is unaffected. Defaults to false.\n"
                  },
                  "allowAnonymousComments": {
                    "type": "boolean",
                    "description": "When `true`, unauthenticated visitors may comment on this release's chats by supplying a display name. Off by default.\n"
                  },
                  "releaseImmediately": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `false`, the release is staged as `unreleased` — it exists and its files are copied, but it stays externally inaccessible (the token's public routes return not-found) until it is released via the `/release` endpoint. Defaults to true (goes live immediately).\n"
                  }
                },
                "required": [
                  "title"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Public file system created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "Forbidden. May be `capabilityNotAvailable` (workspace is on a plan\nthat does not grant `publicSharing` — e.g. Demo) or a permission\nfailure from `canCreatePublicFileSystem`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          }
        ],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Get public file systems for a project",
        "description": "Retrieves a paginated list of public file systems for a project with support for search, filtering, and both cursor and index-based pagination. Requires `canGetPublicFileSystem`. Cursor-only parameters are rejected with index pagination and `page` is rejected with cursor pagination.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Search public file systems by title and description (case-insensitive substring match; whitespace trimmed)."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "active",
                    "expired",
                    "disabled",
                    "unreleased"
                  ]
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "active",
                      "expired",
                      "disabled",
                      "unreleased"
                    ]
                  }
                }
              ],
              "default": [
                "active",
                "expired",
                "unreleased"
              ]
            },
            "description": "Filter by status (single value, comma-separated string or array). The `unreleased` status is manager-only — it is silently stripped for callers without `canCreatePublicFileSystem`."
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by creator ID"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "updatedAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for public file systems"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of results per page"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved public file systems. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PublicFileSystems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedPublicFileSystems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{publicId}": {
      "get": {
        "summary": "Get public file system details",
        "description": "Retrieves detailed information about a specific public file system by its ID. Requires `canGetPublicFileSystem`. The record must belong to `projectId`; a foreign or unknown id returns `publicFileSystemNotFound` (404).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Public file system retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/PublicFileSystemNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the public file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update public file system details",
        "description": "Updates the title, description, validity and sharing flags of an existing public file system. Requires `canUpdatePublicFileSystem`; the record must belong to `projectId`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Updated title for the public file system"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Updated description for the public file system (may be empty)"
                  },
                  "validity": {
                    "type": "number",
                    "minimum": 0,
                    "x-exclusiveMinimum": true,
                    "maximum": 2592000000,
                    "nullable": true,
                    "description": "Updated validity period in milliseconds from now (max 30 days). Pass `null` to remove the expiration."
                  },
                  "hideCreators": {
                    "type": "boolean",
                    "description": "Toggle authorship repression for this release. When `true`, the external public API omits per-item asset/folder creators and the release's \"Shared by\" creator.\n"
                  },
                  "allowAnonymousComments": {
                    "type": "boolean",
                    "description": "Toggle anonymous (display-name only) commenting on this release's chats."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Public file system updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/PublicFileSystemNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the public file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a public file system",
        "description": "Permanently deletes a public file system, its copied file-system entries and its inheritance node, and invalidates its access token. Requires `canDeletePublicFileSystem`; the record must belong to `projectId`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Public file system deleted successfully. Returns the deleted record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/PublicFileSystemNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the public file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{publicId}/add": {
      "post": {
        "summary": "Add items to public file system",
        "description": "Copies additional creator-tree items (assets or folders) into an existing public file system, marks the assets public and recomputes the release inventory. `itemPaths` are relative to `project/{projectId}/creator`. Requires `canAddPubicFileSystemItems`; the record must belong to `projectId`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Creator-tree item paths to add to the public file system",
                    "example": [
                      "Renders/extra.png"
                    ]
                  }
                },
                "required": [
                  "itemPaths"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items added to public file system successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddItemsToPublicFileSystemResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Public file system not found, or an item path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the public file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{publicId}/release": {
      "post": {
        "summary": "Release a staged (unreleased) public file system",
        "description": "Flips a public file system's status from `unreleased` to `active`, making it externally accessible via its public token and recomputing the `hasActivePublicFileSystem` flag on its assets. Requires `canUpdatePublicFileSystem`. Returns `publicNotUnreleased` (400) if the record isn't in the `unreleased` state.",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Public file system released successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (e.g. `publicNotUnreleased`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/PublicFileSystemNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the public file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/files": {
      "get": {
        "summary": "Get items at the root of a public file system (authenticated management)",
        "description": "Lists the items directly under `public/{publicId}` for the release identified by `token`. This is the authenticated management view: unlike the unauthenticated `/public/{token}/files` endpoint it does NOT check token expiration or status, and it never populates chats. Requires `canGetPublicFileSystem`. The token must belong to `projectId`; otherwise `resourceNotFound` (400). Index pagination is the default; pass `paginate=cursor` for cursor pagination (cursor-only parameters are rejected with index pagination and `page` is rejected with cursor pagination).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "If true, includes items in every sub-folder beneath the path instead of only direct children."
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource IDs. May not be combined with `resourceSlugs`."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource slugs. May not be combined with `resourceIds`."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs"
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the resource creator's user ID"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types (single value or array)"
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Partial, case-insensitive match on the resource name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria. Keys are mapped onto the FileSystem entry's resource fields (`name` → `resourceName`, `createdAt` → `resourceCreatedAt`, ...)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of results per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved public file system items. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/files/{path}": {
      "get": {
        "summary": "Get items at a path within a public file system (authenticated management)",
        "description": "Same as the root listing, scoped to a folder path inside the release. Path segments may be folder slugs or IDs (IDs are resolved to slugs; an unknown segment returns `fileSystemPathNotFound`, 404). Requires `canGetPublicFileSystem`; does NOT check token expiration.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Folder path within the tree. May contain several `/`-separated segments; each segment is a folder slug or ID.",
            "example": "Renders/2024"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "If true, includes items in every sub-folder beneath the path instead of only direct children."
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource IDs. May not be combined with `resourceSlugs`."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Filter by specific resource slugs. May not be combined with `resourceIds`."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs"
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the resource creator's user ID"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types (single value or array)"
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Partial, case-insensitive match on the resource name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria. Keys are mapped onto the FileSystem entry's resource fields (`name` → `resourceName`, `createdAt` → `resourceCreatedAt`, ...)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of results per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved public file system items at path. Shape depends on `paginate`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A path segment does not exist (`fileSystemPathNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/files/move": {
      "put": {
        "summary": "Move items within public file system",
        "description": "Moves items to a new location within the public file system. Paths are relative to `public/{publicId}`. Items already in the destination folder are skipped (`count` may be 0). Requires `canMovePublicFileSystemItems`; the token must belong to `projectId`. Does NOT check token expiration.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to move",
                    "example": [
                      "Renders/final.png",
                      "Renders/alt"
                    ]
                  },
                  "destinationPath": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Destination folder path for the items",
                    "example": "Approved"
                  }
                },
                "required": [
                  "itemPaths",
                  "destinationPath"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items moved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A source or destination path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/files/copy": {
      "put": {
        "summary": "Copy items within public file system",
        "description": "Copies items to a new location within the public file system and recomputes the release inventory. Paths are relative to `public/{publicId}`. Requires `canCopyPublicFileSystemItems`; the token must belong to `projectId`. Does NOT check token expiration.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to copy",
                    "example": [
                      "Renders/final.png"
                    ]
                  },
                  "destinationPath": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Destination folder path for the items",
                    "example": "Approved"
                  }
                },
                "required": [
                  "itemPaths",
                  "destinationPath"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items copied successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A source or destination path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/files/delete": {
      "put": {
        "summary": "Delete items from public file system",
        "description": "Deletes items from the public file system. Paths are relative to `public/{publicId}`. Requires `canDeletePublicFileSystemItems`; the token must belong to `projectId`. Does NOT check token expiration.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "itemPaths": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Array of item paths to delete",
                    "example": [
                      "Renders/old.png"
                    ]
                  }
                },
                "required": [
                  "itemPaths"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "An item path does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/files/create-folder": {
      "post": {
        "summary": "Create a folder inside a public file system",
        "description": "Creates a folder at `public/{publicId}/{basePath}` (or the release root when `basePath` is omitted) and recomputes the release inventory. Requires `canCreatePublicFileSystemFolder`; the token must belong to `projectId`. A `basePath` that does not exist in the release returns `basePathNotFound` (404); a folder with the same name already at that address returns `folderExistsAtPath` (409). Does NOT check token expiration.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedNameColorBasePath"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Folder created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "description": "Public file system not found (`publicFileSystemNotFound`) or `basePathNotFound`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A folder with this name already exists at the address (`folderExistsAtPath`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/chat": {
      "get": {
        "summary": "Get main public chat (authenticated management)",
        "description": "Retrieves the main topic chat for a public file system.\nReturns null if no main chat has been created yet (lazy creation pattern).\nUnlike the unauthenticated `/public/{token}/chat` endpoint, this does NOT check\ntoken expiration. Requires `canGetPublicFileSystem` permission.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Chat retrieved successfully (or null if no chat exists)",
            "content": {
              "application/json": {
                "schema": {
                  "nullable": true,
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Chat"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "No release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/assets/{assetId}": {
      "get": {
        "summary": "Get public asset with chat (authenticated management)",
        "description": "Retrieves an asset along with its public chat in the context of this public file system.\nThe chat is returned in `chats.public` and will be null if no chat has been created yet.\nUnlike the unauthenticated `/public/{token}/assets/{assetId}` endpoint, this does NOT\ncheck token expiration. Requires `canGetPublicFileSystem` permission.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Asset retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Asset"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "chats": {
                          "type": "object",
                          "properties": {
                            "public": {
                              "nullable": true,
                              "allOf": [
                                {
                                  "$ref": "#/components/schemas/Chat"
                                }
                              ],
                              "description": "The public chat for this asset (null if not created yet)"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "No release with this token exists in the project (`resourceNotFound`), or the asset record is missing (`assetNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The asset is not part of this public file system (`assetNotInPublicFileSystem`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          },
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the asset to retrieve."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/chat/{chatId}/messages": {
      "get": {
        "summary": "Get public chat messages (authenticated management)",
        "description": "Retrieves paginated messages for a public chat (the release's main topic chat or\none of its asset chats). Unlike the unauthenticated\n`/public/{token}/chat/{chatId}/messages` endpoint, this does NOT check token\nexpiration. Requires `canGetPublicFileSystem` permission. A chat that does not\nbelong to this release returns 403.\n\nResults are always cursor-paginated; `paginate=index` and `page` are accepted\nbut have no effect.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          },
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the chat to get messages from."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for messages"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of messages per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "cursor",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination mode. Cursor-only parameters are rejected when `index`; `page` is rejected when `cursor`."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only accepted with `paginate=index`)."
          },
          {
            "name": "replyLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of recent replies to include per message"
          }
        ],
        "responses": {
          "200": {
            "description": "Messages retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CursorPaginatedChatMessagesWithChat"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The chat does not belong to this public file system.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ChatNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "post": {
        "summary": "Create public chat message (authenticated management)",
        "description": "Creates a message in an existing public chat.\nUnlike the unauthenticated `/public/{token}/chat/{chatId}/messages` endpoint, this does\nNOT check token expiration. Use this for internal management of public file systems.\nRequires `canGetPublicFileSystem` permission. A chat that does not belong to this\nrelease returns 403. Attachments, mentions, asset mentions and quotes are not\nsupported in public chats and are rejected.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10000,
                    "description": "The message content (HTML is sanitised).",
                    "example": "Looks great — approved."
                  },
                  "replyToId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the message being replied to"
                  },
                  "annotations": {
                    "$ref": "#/components/schemas/AnnotationInputList"
                  }
                },
                "required": [
                  "content"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The chat does not belong to this public file system.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ChatNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          },
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the chat to get messages from."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/chat/messages": {
      "post": {
        "summary": "Create public topic chat message with lazy creation (authenticated management)",
        "description": "Creates a message on the main public topic chat. If no main chat exists for this\npublic file system, one is created automatically (lazy creation).\nUnlike the unauthenticated `/public/{token}/chat/messages` endpoint, this does\nNOT check token expiration. Use this for internal management of public file systems.\nRequires `canGetPublicFileSystem` permission. Attachments, mentions, asset mentions\nand quotes are not supported in public chats and are rejected.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10000,
                    "description": "The message content (HTML is sanitised).",
                    "example": "Looks great — approved."
                  },
                  "replyToId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the message being replied to"
                  },
                  "annotations": {
                    "$ref": "#/components/schemas/AnnotationInputList"
                  }
                },
                "required": [
                  "content"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message created successfully (chat created if needed)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chat": {
                      "$ref": "#/components/schemas/Chat"
                    },
                    "message": {
                      "$ref": "#/components/schemas/ChatMessage"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/public/{token}/assets/{assetId}/messages": {
      "post": {
        "summary": "Create public asset chat message with lazy creation (authenticated management)",
        "description": "Creates a message on an asset's public chat. If no public chat exists for the asset\nin this public file system, one is created automatically (lazy creation).\nUnlike the unauthenticated `/public/{token}/assets/{assetId}/messages` endpoint, this does\nNOT check token expiration. Use this for internal management of public file systems.\nRequires `canGetPublicFileSystem` permission. Attachments, mentions, asset mentions\nand quotes are not supported in public chats and are rejected.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10000,
                    "description": "The message content (HTML is sanitised).",
                    "example": "Looks great — approved."
                  },
                  "replyToId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the message being replied to"
                  },
                  "annotations": {
                    "$ref": "#/components/schemas/AnnotationInputList"
                  }
                },
                "required": [
                  "content"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message created successfully (chat created if needed)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chat": {
                      "$ref": "#/components/schemas/Chat"
                    },
                    "message": {
                      "$ref": "#/components/schemas/ChatMessage"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or no release with this token exists in the project (`resourceNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The asset is not part of this public file system (`assetNotInPublicFileSystem`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$"
            },
            "description": "The public access token of the file system. Must belong to `projectId`."
          },
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the asset."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/creator/highlighted-messages": {
      "get": {
        "summary": "Get highlighted messages across the project's creator-visible chats",
        "description": "Cursor-paginated list of active chat messages flagged `highlighted` whose scope is this project and whose scope visibility includes `creator`. Each message is populated (author, attachments, etc.) and carries its parent `chat`. Requires `canGetCreatorHighlights`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "createdAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for highlighted messages"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of messages per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Highlighted messages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CursorPaginatedChatMessagesWithChat"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/reviewer/highlighted-messages": {
      "get": {
        "summary": "Get highlighted messages across the project's reviewer-visible chats",
        "description": "Cursor-paginated list of active chat messages flagged `highlighted` whose scope is this project and whose scope visibility includes `reviewer`. Each message is populated (author, attachments, etc.) and carries its parent `chat`. Requires `canGetReviewerHighlights`.\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "createdAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sorting criteria for highlighted messages"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of messages per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Highlighted messages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CursorPaginatedChatMessagesWithChat"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/projects/{projectId}/settings/{name}": {
      "patch": {
        "summary": "Update a single project setting",
        "description": "Updates one key of the project's `settings` JSON column. Mirrors the\nworkspace settings endpoint. Requires `canManageProjectSettings`\n(no subscription check on this route).\n\nAt the project level every setting accepts `null`, meaning \"inherit the\nworkspace's value\". Otherwise the type of `value` depends on `name`:\n\n- `allowAiFeatures`, `aiPolishEnabled`, `aiComposeEnabled`, `aiChatEnabled`,\n  `aiAssistEnabled`, `aiTaskGenerationEnabled`, `aiImageRevisionEnabled`,\n  `aiPolishAllowReviewer`, `aiComposeAllowReviewer`, `aiChatAllowReviewer`,\n  `aiAssistAllowReviewer`, `aiTaskGenerationAllowReviewer`,\n  `aiImageRevisionAllowReviewer` — boolean.\n- `aiAssistFollowUpWindow` — integer, 0–30.\n- `aiCustomPreprompt`, `aiTaskGenerationPreprompt` — string (may be empty; empty means \"no preprompt\", null means inherit).\n- `aiChatTools`, `aiAssistTools` — object mapping tool names (`^[a-z][a-z0-9_]*$`) to boolean or null.\n\nA `value` of the wrong type for the setting is a validation error (400).\n",
        "tags": [
          "Projects"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "value": {
                    "description": "The new value. `null` inherits the workspace's value. Otherwise the type must match the setting named in the path: boolean for the enable / allow-reviewer flags, integer 0–30 for `aiAssistFollowUpWindow`, string (max preprompt length, may be empty) for `aiCustomPreprompt` / `aiTaskGenerationPreprompt`, and an object of tool-name → boolean|null for `aiChatTools` / `aiAssistTools`.\n"
                  }
                },
                "required": [
                  "value"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (unknown setting name or wrong value type).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the project."
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "allowAiFeatures",
                "aiPolishEnabled",
                "aiComposeEnabled",
                "aiChatEnabled",
                "aiAssistEnabled",
                "aiAssistFollowUpWindow",
                "aiTaskGenerationEnabled",
                "aiImageRevisionEnabled",
                "aiPolishAllowReviewer",
                "aiComposeAllowReviewer",
                "aiChatAllowReviewer",
                "aiAssistAllowReviewer",
                "aiTaskGenerationAllowReviewer",
                "aiImageRevisionAllowReviewer",
                "aiCustomPreprompt",
                "aiTaskGenerationPreprompt",
                "aiChatTools",
                "aiAssistTools"
              ]
            },
            "description": "The project setting to update. Only the listed names are accepted."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public/{token}": {
      "get": {
        "summary": "Get public file system details",
        "description": "Retrieves detailed information about a public file system using its access token. No authentication required. The token must resolve to an `active`, unexpired release. When the release was created with `hideCreators: true`, the `creator` and `creatorId` fields (\"Shared by\") are omitted from the response.\n",
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "description": "Public file system retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`)"
          },
          "404": {
            "description": "Public file system not found, token invalid, or release not active (`notFound`)"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public/{token}/files": {
      "get": {
        "summary": "Get public file system items",
        "description": "Retrieves items (assets and folders) from a public file system at the root level. No authentication required. Supports index pagination (default) or cursor pagination via `paginate=cursor`; the cursor-only options are rejected with 400 when `paginate` is `index`, and `page` is rejected when `paginate` is `cursor`. Populated resources are reduced to the public allowlist before being returned. When the release was created with `hideCreators: true`, each item's `creator` / `resourceCreator` / `resourceCreatorId` fields are omitted.\n",
        "tags": [
          "Public"
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type to use"
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Whether to search recursively through subfolders"
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Restrict results to these asset/folder IDs. A single value or repeated parameter."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Restrict results to these resource slugs. A single value or repeated parameter."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs. A single value or repeated parameter."
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the ID of the user who created the resource"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types. A single value or repeated parameter."
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Search items by name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order, e.g. `sort[name]=1`. Keys are mapped onto the underlying file-system item fields."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of items per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (cursor pagination only; rejected when `paginate=index`)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to paginate in reverse order (cursor pagination only)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include total counts (cursor pagination only)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include the cursor record itself (cursor pagination only)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "Start at a specific file-system item ID (cursor pagination only)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include the startAt record (cursor pagination only)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (index pagination only; rejected when `paginate=cursor`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Items retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/PublicCursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`)"
          },
          "404": {
            "description": "Public file system not found, token invalid, or release not active"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/public/{token}/files/{path}": {
      "get": {
        "summary": "Get public file system items at path",
        "description": "Retrieves items (assets and folders) from a public file system at a specific folder path. No authentication required. Accepts the same query parameters as `GET /public/{token}/files`. When the release was created with `hideCreators: true`, each item's creator fields are omitted.\n",
        "tags": [
          "Public"
        ],
        "parameters": [
          {
            "name": "0",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          },
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The folder path to browse (supports nested paths). Segments may be folder slugs or folder IDs; IDs are resolved to slugs before lookup.\n"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type to use"
          },
          {
            "name": "recursiveSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Whether to search recursively through subfolders"
          },
          {
            "name": "resourceIds",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Restrict results to these asset/folder IDs. A single value or repeated parameter."
          },
          {
            "name": "resourceSlugs",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9-]+$"
                  }
                },
                {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              ],
              "default": []
            },
            "description": "Restrict results to these resource slugs. A single value or repeated parameter."
          },
          {
            "name": "resourceType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "folder"
              ]
            },
            "description": "Filter by resource type"
          },
          {
            "name": "resourceTags",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                {
                  "type": "string",
                  "format": "uuid"
                }
              ],
              "default": []
            },
            "description": "Filter by tag IDs. A single value or repeated parameter."
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by the ID of the user who created the resource"
          },
          {
            "name": "mediaTypes",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "image",
                      "video",
                      "audio",
                      "folder",
                      "file"
                    ]
                  }
                },
                {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "folder",
                    "file"
                  ]
                }
              ],
              "default": []
            },
            "description": "Filter by media types. A single value or repeated parameter."
          },
          {
            "name": "resourceStatus",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active",
              "enum": [
                "active",
                "pendingDelete"
              ]
            },
            "description": "Filter by resource status"
          },
          {
            "name": "nameSearch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Search items by name"
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "mediaType": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "status": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "sizeInBytes": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order, e.g. `sort[name]=1`. Keys are mapped onto the underlying file-system item fields."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Number of items per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (cursor pagination only; rejected when `paginate=index`)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to paginate in reverse order (cursor pagination only)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include total counts (cursor pagination only)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include the cursor record itself (cursor pagination only)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "Start at a specific file-system item ID (cursor pagination only)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include the startAt record (cursor pagination only)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "minimum": 1,
              "x-conditionally-required": true
            },
            "description": "Page number (index pagination only; rejected when `paginate=cursor`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Items retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FileSystemItems"
                    },
                    {
                      "$ref": "#/components/schemas/PublicCursorPaginatedFileSystemItems"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`)"
          },
          "404": {
            "description": "Public file system not found, token invalid, release not active, or path not found"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/public/{token}/download": {
      "post": {
        "summary": "Generate download links for assets",
        "description": "Generates signed download URLs for the original files of assets in a public file system. No authentication required. Every requested asset must exist within the release's file-system path, otherwise the whole request is rejected with 400 `assetNotFound`. One result object is returned per requested asset; an individual entry may carry `status: fail` (with an `error` code) when its original file could not be signed.\n",
        "tags": [
          "Public"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "assetIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1,
                    "maxItems": 100,
                    "description": "Array of asset IDs to generate download links for"
                  }
                },
                "required": [
                  "assetIds"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Download links generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/DownloadSignedUrlData"
                      },
                      {
                        "type": "object",
                        "description": "Per-asset failure entry",
                        "properties": {
                          "ownerAssetId": {
                            "$ref": "#/components/schemas/UUID"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "fail"
                            ]
                          },
                          "error": {
                            "type": "string",
                            "example": "originalNotFound"
                          }
                        }
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or one or more assets are not part of this public file system (`assetNotFound`)"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`)"
          },
          "404": {
            "description": "Public file system not found, token invalid, or release not active"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public/{token}/chat": {
      "get": {
        "summary": "Get main public file system chat",
        "description": "Retrieves the main topic chat for a public file system.\nReturns null if no main chat has been created yet (lazy creation pattern).\nNo authentication required for read access.\n",
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "description": "Chat retrieved successfully (or null if no chat exists)",
            "content": {
              "application/json": {
                "schema": {
                  "nullable": true,
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Chat"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`) or the release is disabled (`publicDisabled`)"
          },
          "404": {
            "description": "Public file system not found or token invalid (`publicNotFound`)"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          },
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public/{token}/chat/messages": {
      "post": {
        "summary": "Create message on the main public chat (lazy creation)",
        "description": "Creates a message on the public file system's main topic chat. If the\nmain chat does not exist yet it is created automatically (lazy creation\npattern), using the release title as its subject.\n\n**Authentication is optional.** A signed-in user posts with their real\nidentity (`authorId`); an unauthenticated visitor may post anonymously by\nsupplying `guestName` when the release has `allowAnonymousComments` enabled.\nSee the `guestId` / `guestColor` notes on `POST /public/{token}/chat/{chatId}/messages`.\nAttachments, mentions, asset mentions and quotes are not supported in\npublic chats and are rejected with 400.\n",
        "tags": [
          "Public"
        ],
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedContentReplyToIdAnnotations"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chat and message created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chat": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Chat"
                        }
                      ],
                      "description": "The main topic chat (created if it didn't exist)"
                    },
                    "message": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ChatMessage"
                        }
                      ],
                      "description": "The created message"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or a display name is required to comment anonymously (`guestNameRequired`)"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`), the release is disabled\n(`publicDisabled`), or anonymous comments are not enabled for this\nrelease (`anonymousCommentsDisabled`).\n"
          },
          "404": {
            "description": "Public file system not found or token invalid (`publicNotFound`)"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public/{token}/chat/{chatId}/messages": {
      "get": {
        "summary": "Get public chat messages",
        "description": "Retrieves paginated messages for a public chat. The chat must be the\nrelease's main topic chat or one of its asset public chats, otherwise\n403 `forbidden` is returned.\nNo authentication required for read access.\n\nMessages are always returned cursor-paginated. `paginate=index` and\n`page` are accepted by validation but are ignored by the handler.\n",
        "tags": [
          "Public"
        ],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          },
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the chat to get messages from or post to."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "id": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort order, e.g. `sort[createdAt]=-1`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Number of messages per page"
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "cursor",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination style. Only `cursor` is honoured by the handler."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (cursor mode only)"
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to paginate in reverse order (cursor mode only)"
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include total counts (cursor mode only)"
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include the cursor record itself (cursor mode only)"
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "Start at specific message ID (cursor mode only)"
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "Whether to include the startAt record (cursor mode only)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (index mode only; accepted but ignored)"
          },
          {
            "name": "replies",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 10
            },
            "description": "Number of recent replies to include per message"
          }
        ],
        "responses": {
          "200": {
            "description": "Messages retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CursorPaginatedMessages"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`), release is disabled (`publicDisabled`), or the chat does not belong to this release (`forbidden`)"
          },
          "404": {
            "description": "Public file system not found (`publicNotFound`) or chat not found (`chatNotFound`)"
          }
        },
        "x-request-source": "joi"
      },
      "post": {
        "summary": "Create message in public chat",
        "description": "Creates a new message in an existing public chat. The chat must be the\nrelease's main topic chat or one of its asset public chats.\n\n**Authentication is optional.** A signed-in user posts with their real\nidentity (`authorId`). An unauthenticated visitor may post anonymously by\nsupplying a display name (`guestName`) — but only when the release has\n`allowAnonymousComments` enabled. The client should also send a stable\n`guestId` (a UUID it persists in localStorage per token) so two guests who\ntype the same name stay distinct, plus an optional `guestColor`. Guest\nfields are ignored when the caller is authenticated.\nAttachments, mentions, asset mentions and quotes are not supported in\npublic chats and are rejected with 400.\n",
        "tags": [
          "Public"
        ],
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedContentReplyToIdAnnotations"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or a display name is required to comment anonymously (`guestNameRequired`)"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`), the release is disabled\n(`publicDisabled`), anonymous comments are not enabled for this\nrelease (`anonymousCommentsDisabled`), or the chat does not belong to\nthis release (`forbidden`).\n"
          },
          "404": {
            "description": "Public file system not found (`publicNotFound`) or chat not found (`chatNotFound`)"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          },
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the chat to get messages from or post to."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public/{token}/assets/{assetId}": {
      "get": {
        "summary": "Get asset with public chat",
        "description": "Retrieves an asset along with its public chat in the context of this public file system.\nThe chat is returned in `chats.public` and will be null if no chat has been created yet.\nNo authentication required for read access.\nWhen the release was created with `hideCreators: true`, the asset's creator fields are omitted.\n",
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "description": "Asset retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Asset"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "chats": {
                          "type": "object",
                          "properties": {
                            "public": {
                              "nullable": true,
                              "allOf": [
                                {
                                  "$ref": "#/components/schemas/Chat"
                                }
                              ],
                              "description": "The public chat for this asset (null if not created yet)"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Asset record not found (`assetNotFound`)"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`) or the release is disabled (`publicDisabled`)"
          },
          "404": {
            "description": "Public file system not found (`publicNotFound`) or asset is not part of this public file system (`assetNotInPublicFileSystem`)"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          },
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the asset to retrieve."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public/{token}/assets/{assetId}/messages": {
      "post": {
        "summary": "Create message on asset's public chat (lazy creation)",
        "description": "Creates a message on an asset's public chat. If no public chat exists for this asset\nin this public file system, one is created automatically (lazy creation pattern).\n\n**Authentication is optional.** A signed-in user posts with their real\nidentity (`authorId`); an unauthenticated visitor may post anonymously by\nsupplying `guestName` when the release has `allowAnonymousComments` enabled.\nSee the `guestId` / `guestColor` notes on `POST /public/{token}/chat/{chatId}/messages`.\nAttachments, mentions, asset mentions and quotes are not supported in\npublic chats and are rejected with 400.\n",
        "tags": [
          "Public"
        ],
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedContentReplyToIdAnnotations"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chat and message created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chat": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Chat"
                        }
                      ],
                      "description": "The chat (created if it didn't exist)"
                    },
                    "message": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ChatMessage"
                        }
                      ],
                      "description": "The created message"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or a display name is required to comment anonymously (`guestNameRequired`)"
          },
          "403": {
            "description": "Token is expired (`publicTokenExpired`), the release is disabled\n(`publicDisabled`), or anonymous comments are not enabled for this\nrelease (`anonymousCommentsDisabled`).\n"
          },
          "404": {
            "description": "Public file system not found (`publicNotFound`) or asset is not part of this public file system (`assetNotInPublicFileSystem`)"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]{10}$",
              "example": "abc123xyz0"
            },
            "description": "The 10-character access token for the public file system."
          },
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the asset to post to."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/public-links": {
      "post": {
        "summary": "Create a public download link for an asset",
        "description": "Creates a temporary public download link for an asset. The link can optionally have\nan expiration. Requires the `canCreatePublicLink` permission (project/workspace admin roles)\nAND the `publicSharing` workspace capability — granted by every paid base plan and\nintentionally withheld from the Demo plan. Workspaces without the capability receive\n403 `capabilityNotAvailable` with `errorData.capability = \"publicSharing\"`, which\nclients can use to prompt for an upgrade.\n\nSets `hasActivePublicLink`, `everPublic`, and `publicAssetLinkIds` on the asset.\nSends a notification and email to workspace admins if the creator is a project-level admin.\n",
        "tags": [
          "PublicAssetLinks",
          "Assets"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to create a public link for."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "validity": {
                    "type": "integer",
                    "minimum": 60000,
                    "description": "Link validity in milliseconds. If omitted, the link does not expire.\n",
                    "example": 86400000
                  },
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The project ID the asset belongs to."
                  },
                  "mode": {
                    "type": "string",
                    "default": "embed-download",
                    "enum": [
                      "download",
                      "embed",
                      "embed-download"
                    ],
                    "description": "Capability mode for the link. Enforced server-side at\n`/v1/public-download/{token}/download` and `/embed-files`.\n- `download` — direct-download link only; embed iframe blocked.\n- `embed` — embeddable iframe only; direct download blocked.\n- `embed-download` — both endpoints allowed (default; backward-compatible).\n"
                  }
                },
                "required": [
                  "projectId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Public link created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAssetLink"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - Invalid asset ID or request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Authentication required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. May be `capabilityNotAvailable` (workspace is on a plan\nthat does not grant `publicSharing` — e.g. Demo) or a permission\nfailure from `canCreatePublicLink`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "List all public download links for an asset",
        "description": "Returns all public download links for an asset, including active, disabled, and expired links.\nRequires canGetPublicLinks permission.\n",
        "tags": [
          "PublicAssetLinks",
          "Assets"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset."
          }
        ],
        "responses": {
          "200": {
            "description": "List of public links.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PublicAssetLink"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Authentication required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - User does not have canGetPublicLinks permission.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/public-links/{linkId}": {
      "put": {
        "summary": "Update a public download link",
        "description": "Update a public link's expiration or status. At least one field must be provided.\nRequires canUpdatePublicLink permission.\n",
        "tags": [
          "PublicAssetLinks"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "linkId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "validity": {
                    "type": "integer",
                    "minimum": 60000,
                    "description": "New validity in milliseconds from now."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "disabled"
                    ],
                    "description": "New status for the link."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "download",
                      "embed",
                      "embed-download"
                    ],
                    "description": "Update the link's capability mode."
                  }
                },
                "additionalProperties": false,
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Link updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAssetLink"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Public asset link not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/public-links/{linkId}/disable": {
      "put": {
        "summary": "Disable a public download link",
        "description": "Sets the link status to 'disabled'. The link can no longer be used for downloads.\nUpdates hasActivePublicLink on the asset. Requires canUpdatePublicLink permission.\n",
        "tags": [
          "PublicAssetLinks"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "linkId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Link disabled successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAssetLink"
                }
              }
            }
          },
          "404": {
            "description": "Public asset link not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/public-links/{linkId}/reactivate": {
      "put": {
        "summary": "Reactivate a disabled or expired public download link",
        "description": "Sets the link status back to 'active' with an optional new expiration.\nUpdates hasActivePublicLink on the asset. Requires canUpdatePublicLink permission.\n",
        "tags": [
          "PublicAssetLinks"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "linkId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "validity": {
                    "type": "integer",
                    "minimum": 60000,
                    "description": "New validity in milliseconds from now. If omitted, no expiration."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Link reactivated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicAssetLink"
                }
              }
            }
          },
          "404": {
            "description": "Public asset link not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/public-download/{token}": {
      "get": {
        "summary": "Resolve a public download token",
        "description": "Resolves a public download token to minimal file information for the download page.\nNo authentication required. Returns fileName, mediaType, status, the link mode,\nand (for image assets) a `previewKeyPath` so the page can render an inline preview.\n",
        "tags": [
          "PublicAssetLinks"
        ],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]+$",
              "example": "Ab3dEf9HiJ"
            },
            "description": "The 10-character alphanumeric public download token."
          }
        ],
        "responses": {
          "200": {
            "description": "Token resolved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "The public download token."
                    },
                    "fileName": {
                      "type": "string",
                      "description": "Name of the file.",
                      "example": "my-video.mp4"
                    },
                    "mediaType": {
                      "type": "string",
                      "description": "Media type of the asset.",
                      "example": "video"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active"
                      ],
                      "description": "Status of the link."
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "download",
                        "embed",
                        "embed-download"
                      ],
                      "description": "Capability mode of the link. Clients use this to decide whether to\nrender an inline preview (`embed-download`), an embed-only\nmessage (`embed`), or start the download directly (`download`).\n",
                      "example": "embed-download"
                    },
                    "mimeType": {
                      "type": "string",
                      "nullable": true,
                      "description": "Mime type of the original media file, when known.",
                      "example": "image/png"
                    },
                    "previewKeyPath": {
                      "type": "string",
                      "nullable": true,
                      "description": "For image assets: keyPath of the image file. Resolve it against the\nbase media URL to render an inline preview. `null` for non-image\nassets (video/audio previews embed the player page; other files\nrender an icon).\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The underlying asset is no longer active (`assetNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Public asset link not found (`publicAssetLinkNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Link has expired (`publicAssetLinkExpired`) or is disabled (`publicAssetLinkInactive`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/public-download/{token}/download": {
      "get": {
        "summary": "Get a signed download URL for a public download token",
        "description": "Generates a signed download URL for the asset's original file.\nNo authentication required. The URL is temporary and expires.\n",
        "tags": [
          "PublicAssetLinks"
        ],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]+$"
            },
            "description": "The 10-character alphanumeric public download token."
          }
        ],
        "responses": {
          "200": {
            "description": "Signed download URL generated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "Signed download URL."
                    },
                    "fileName": {
                      "type": "string",
                      "description": "Name of the file for download."
                    },
                    "mimeType": {
                      "type": "string",
                      "description": "MIME type of the file.",
                      "example": "video/mp4"
                    },
                    "expires": {
                      "type": "integer",
                      "description": "Unix timestamp (seconds) when the signed URL expires."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The link is `embed`-only and does not allow direct downloads (`publicAssetLinkEmbedOnly`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Public asset link not found (`publicAssetLinkNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Link has expired (`publicAssetLinkExpired`) or is disabled (`publicAssetLinkInactive`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The asset or its original file is missing, so no signed URL could be produced (`unknownError`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/public-download/{token}/embed-files": {
      "get": {
        "summary": "Resolve a public asset link to embed-player file paths",
        "description": "Returns the HLS streaming manifest keyPath and a fallback original-media keyPath\nfor the asset behind a public download token. This is what the embeddable\nplayer at `/public/{token}/embed` uses.\n\nResolve keyPaths against the base media URL. Play the `.m3u8` manifest with an\nHLS-capable player when `streamKeyPath` is present, falling back to the original\nmedia file otherwise. Only `video` and `audio` assets are meant to be embedded;\nthe response still returns paths for other media types, but they should not be\nembedded.\n\nNo authentication required. Returns minimal file info — never the full asset\nrecord (no `id`, `files`, `creatorId`, etc.).\n",
        "tags": [
          "PublicAssetLinks"
        ],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 10,
              "maxLength": 10,
              "pattern": "^[a-zA-Z0-9]+$",
              "example": "Ab3dEf9HiJ"
            },
            "description": "The 10-character alphanumeric public download token."
          }
        ],
        "responses": {
          "200": {
            "description": "Embed file paths resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "fileName",
                    "mediaType",
                    "streamKeyPath",
                    "mediaKeyPath",
                    "posterKeyPath",
                    "waveformKeyPath"
                  ],
                  "properties": {
                    "fileName": {
                      "type": "string",
                      "description": "Asset name; use as the iframe `title` for accessibility.",
                      "example": "my-video.mp4"
                    },
                    "mediaType": {
                      "type": "string",
                      "enum": [
                        "video",
                        "audio",
                        "image",
                        "file"
                      ],
                      "description": "Media type of the asset. Embed UI is only exposed for `video` and `audio`;\nother types are returned for completeness but should not be embedded.\n",
                      "example": "video"
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "embed",
                        "embed-download"
                      ],
                      "description": "Capability mode of the underlying public link. `embed-download` allows\nboth embedding and direct download; `embed` blocks direct download.\n`download`-only links never reach this endpoint (rejected with 403).\nClients can use this to show or hide a download control.\n",
                      "example": "embed-download"
                    },
                    "streamKeyPath": {
                      "type": "string",
                      "nullable": true,
                      "description": "HLS manifest keyPath (e.g. `workspaces/{ws}/.../stream/index.m3u8`),\n`null` when post-processing has not produced an HLS rendition yet.\nResolve it against the base media URL and play it with an HLS-capable player.\n",
                      "example": "/workspaces/abc/assets/123/stream/index.m3u8"
                    },
                    "mediaKeyPath": {
                      "type": "string",
                      "nullable": true,
                      "description": "Original media file keyPath, used as the fallback when `streamKeyPath` is null.\n`null` when the asset has not finished uploading or its files are inactive.\n",
                      "example": "/workspaces/abc/assets/123/media/my-video.mp4"
                    },
                    "posterKeyPath": {
                      "type": "string",
                      "nullable": true,
                      "description": "Poster image keyPath to use as the video poster so the embed\nshows a still frame before playback starts. Precedence: user-supplied\n`customThumbnail` (largest by size), then explicit `poster` file, then largest\nauto-generated `thumbnail` file. `null` when none is available (e.g. audio-only\nassets, or video with no generated thumbnails yet).\n\nWhen the picked poster is a `customThumbnail`, the keyPath is suffixed with\n`?v={fileId}` as a cache-buster — custom thumbnails reuse the same storage path\nacross re-uploads, and CDNs/browsers would otherwise serve the previous\nversion indefinitely.\n",
                      "example": "/workspaces/abc/assets/123/customThumbnail/large.jpg?v=019e10aa-0000-7000-8000-000000000002"
                    },
                    "waveformKeyPath": {
                      "type": "string",
                      "nullable": true,
                      "description": "Pre-computed waveform peaks keyPath (audio assets), so an audio player can\nrender the waveform without decoding the stream. `null` for non-audio\nassets or audio that hasn't been post-processed.\n",
                      "example": "/workspaces/abc/assets/123/waveform/peaks.json"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The underlying asset is no longer active (`assetNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The link is `download`-only and does not allow embedding (`publicAssetLinkDownloadOnly`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Public asset link not found (`publicAssetLinkNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Link has expired (`publicAssetLinkExpired`) or is disabled (`publicAssetLinkInactive`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/scratch/{scratchId}/complete-upload": {
      "post": {
        "tags": [
          "Scratch"
        ],
        "summary": "Finish a multipart upload for a scratch object.",
        "description": "Same body as `POST /v1/assets/complete-upload`. Commits the\nmultipart upload, records the final object size against the\nworkspace's storage usage, and moves the scratch object to\n`active`. Only the user who created the scratch object may call\nthis (403 otherwise). Idempotent: calling it on an\nalready-`active` object returns the same `{ id, url }` without\nchanging anything.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "scratchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "uploadId": {
                    "type": "string",
                    "description": "Multipart upload ID returned with the signed upload URL bundle."
                  },
                  "parts": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "ETag": {
                          "type": "string"
                        },
                        "PartNumber": {
                          "type": "integer",
                          "minimum": 1
                        }
                      },
                      "required": [
                        "ETag",
                        "PartNumber"
                      ],
                      "additionalProperties": false
                    },
                    "minItems": 1
                  }
                },
                "required": [
                  "uploadId",
                  "parts"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload committed. The scratch object is now `active` and its size counts toward workspace storage.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "url": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `fileInvalid` if `uploadId` or `parts` is\nmissing/empty. `notSupported` if the row is neither\n`pendingUpload` nor `active` (e.g. already promoted / expired).\n"
          },
          "403": {
            "description": "Caller is not the creator."
          },
          "404": {
            "description": "Scratch object not found, or the uploaded object could not be verified in storage."
          },
          "500": {
            "description": "`unknownError` if the multipart upload could not be committed."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/scratch/{scratchId}/promote": {
      "post": {
        "tags": [
          "Scratch"
        ],
        "summary": "Promote a scratch object to a real Asset.",
        "description": "Materialises an `active` scratch object as an Asset\n(`functionType: media`) in the workspace or project the scratch\nobject was created in. The destination is derived from the scratch\nobject itself — a project asset when it was created within a\nproject, otherwise a workspace asset — and is **not** accepted from\nthe request body, so a client cannot claim a scratch object belongs\nto a different workspace or project.\n\nOnly the user who created the scratch object may promote it, and\nthey must also hold `canCreateAsset` on the destination workspace\nor project.\n\nIdempotent: promoting an object that was already promoted returns\nthe existing asset with `alreadyPromoted: true`, so a client can\ntell \"freshly added\" from \"already added\". To attach a scratch\nobject to a chat message instead, pass it as a `{ scratchId, name? }`\nattachment when sending the message\n(`POST /v1/chats/{chatId}/messages`).\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "scratchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fileName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Optional user-facing name for the resulting Asset. Defaults to the scratch object's own name.",
                    "example": "hero-shot-v2.png"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scratch promoted (or already promoted).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "asset",
                    "alreadyPromoted"
                  ],
                  "properties": {
                    "asset": {
                      "$ref": "#/components/schemas/Asset"
                    },
                    "alreadyPromoted": {
                      "type": "boolean",
                      "description": "`true` when the scratch object had already been promoted and the existing asset is returned."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Caller is not the creator, or lacks `canCreateAsset` on the destination resource (`forbidden`)."
          },
          "404": {
            "description": "Scratch not found (`notFound`)."
          },
          "500": {
            "description": "Promotion failed for a non-API reason (`aiRevisionPromotionFailed`)."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/settings": {
      "get": {
        "summary": "Get all resource settings",
        "description": "Returns all resource-specific setting overrides for the current user",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved all resource settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resourceSettings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ResourceSettings"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/settings/{resourceType}/{resourceId}/effective": {
      "get": {
        "summary": "Get effective settings for a resource",
        "description": "Returns resolved settings with cascade from global (user preferences) → workspace → project. Only the caller's own settings are resolved.",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved effective settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EffectiveSettings"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/UserNotFound"
          }
        },
        "parameters": [
          {
            "name": "resourceType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "workspace",
                "project"
              ]
            },
            "description": "The resource type (workspace or project)"
          },
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The resource ID"
          },
          {
            "name": "workspaceId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Parent workspace id. Only meaningful when resourceType is 'project' — when supplied, that workspace's overrides are applied between the global and project layers; when omitted the workspace layer is skipped."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/settings/{resourceType}/{resourceId}": {
      "get": {
        "summary": "Get raw resource settings",
        "description": "Returns only the caller's overrides for this specific resource (not cascaded). When no overrides exist a `{ message }` object is returned instead (still 200).",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved resource settings",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ResourceSettings"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "No custom settings for this resource"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/UserNotFound"
          }
        },
        "parameters": [
          {
            "name": "resourceType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "workspace",
                "project"
              ]
            },
            "description": "The resource type (workspace or project)"
          },
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The resource ID"
          }
        ],
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update resource settings",
        "description": "Create or update the caller's setting overrides for a specific resource. Each supplied category is shallow-merged into the existing overrides for that resource; at least one category is required. No check is made that the caller is a member of the resource.",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emailNotifications": {
                    "type": "object",
                    "additionalProperties": {
                      "oneOf": [
                        {
                          "type": "boolean"
                        },
                        {
                          "type": "number"
                        },
                        {
                          "nullable": true
                        }
                      ]
                    }
                  },
                  "systemNotifications": {
                    "type": "object",
                    "additionalProperties": {
                      "oneOf": [
                        {
                          "type": "boolean"
                        },
                        {
                          "nullable": true
                        }
                      ]
                    }
                  },
                  "sounds": {
                    "type": "object",
                    "additionalProperties": {
                      "oneOf": [
                        {
                          "type": "boolean"
                        },
                        {
                          "nullable": true
                        }
                      ]
                    }
                  }
                },
                "additionalProperties": false,
                "minProperties": 1,
                "description": "Body for `PUT /settings/{resourceType}/{resourceId}`. Each category is a\nfree-form map — any setting key is accepted (the well-known keys are\nlisted in `ResourceSettingsOverrides`). `null` clears an override so the\nparent level applies again. `emailNotifications` values may be boolean,\nnumber (interval settings) or null; the other categories accept boolean\nor null.\n",
                "example": {
                  "emailNotifications": {
                    "newMention": false,
                    "newMentionActiveInterval": 30
                  },
                  "sounds": {
                    "playSounds": null
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated resource settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "settings": {
                      "$ref": "#/components/schemas/ResourceSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [
          {
            "name": "resourceType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "workspace",
                "project"
              ]
            },
            "description": "The resource type (workspace or project)"
          },
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The resource ID"
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Reset resource settings",
        "description": "Remove all setting overrides for this resource",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully reset resource settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [
          {
            "name": "resourceType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "workspace",
                "project"
              ]
            },
            "description": "The resource type (workspace or project)"
          },
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The resource ID"
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/shortlink/{code}": {
      "get": {
        "summary": "Resolve a short link",
        "description": "Resolves a short link code to its full deep link. This endpoint is publicly accessible\nand does not require authentication. The deep link can be used to navigate directly to\na resource in the Nurama web app.\n",
        "tags": [
          "ShortLinks"
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 8,
              "pattern": "^[a-zA-Z0-9]+$",
              "example": "Ab3dEf9H"
            },
            "description": "The 8-character alphanumeric short link code."
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully resolved the short link.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deepLink": {
                      "type": "string",
                      "description": "The full deep link URL for navigation (an absolute URL on the Nurama web app origin).",
                      "example": "https://app.nurama.com/workspaces/my-workspace/projects/my-project/creator/assets/019e10aa-0000-7000-8000-000000000001"
                    },
                    "resourceType": {
                      "type": "string",
                      "enum": [
                        "asset",
                        "project",
                        "workspace",
                        "chatMessage",
                        "chatSubmission"
                      ],
                      "description": "The type of the linked resource. Links created via the API are `asset` or `chatMessage`.",
                      "example": "asset"
                    },
                    "visibility": {
                      "type": "string",
                      "enum": [
                        "creator",
                        "reviewer",
                        null
                      ],
                      "nullable": true,
                      "description": "The visibility context for the resource (if applicable).",
                      "example": "creator"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - Invalid short link code format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Short link not found (`shortLinkNotFound`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/assets/{assetId}/shortlink": {
      "post": {
        "summary": "Create a short link for an asset",
        "description": "Creates a short link for an asset. If a short link already exists for the asset\nwith the same visibility setting, the existing short link is returned.\nRequires `canGetAsset` on the asset.\n",
        "tags": [
          "ShortLinks",
          "Assets"
        ],
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the asset to create a short link for."
          },
          {
            "name": "visibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            },
            "description": "Optional visibility context for the short link. If specified, the deep link\nwill include this visibility in the path. If not specified, the deep link\nwill use the default visibility for the resource.\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Short link created or existing short link returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "pattern": "^[a-zA-Z0-9]{8}$",
                      "description": "The 8-character alphanumeric short link code.",
                      "example": "Ab3dEf9H"
                    },
                    "shortUrl": {
                      "type": "string",
                      "description": "The complete short URL ready for sharing.",
                      "example": "https://app.nurama.com/s/Ab3dEf9H"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - Invalid asset ID or visibility.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Authentication required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - User does not have permission to access the asset.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/chats/message/{messageId}/shortlink": {
      "post": {
        "summary": "Create a short link for a chat message",
        "description": "Creates a short link for a chat message. If a short link already exists for the message,\nthe existing short link is returned. Visibility is automatically inherited from the\nparent chat (e.g., 'creator' or 'reviewer' for topic chats, null for member chats).\nRequires `canCreateMessageShortLink` (read access to the message's chat).\n",
        "tags": [
          "ShortLinks",
          "Chats"
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of the chat message to create a short link for."
          }
        ],
        "responses": {
          "201": {
            "description": "Short link created or existing short link returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "pattern": "^[a-zA-Z0-9]{8}$",
                      "description": "The 8-character alphanumeric short link code.",
                      "example": "Xk7mPq2R"
                    },
                    "shortUrl": {
                      "type": "string",
                      "description": "The complete short URL ready for sharing.",
                      "example": "https://app.nurama.com/s/Xk7mPq2R"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Authentication required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden - User does not have permission to access the chat message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/storage/chart/{resourceType}/{resourceId}": {
      "get": {
        "summary": "Get storage chart data",
        "description": "Returns the average `sizeInBytes` of a resource's storage records per aggregation period between `startDate` and `endDate`, with missing periods filled from the previous value. Allowed when requesting your own user storage, or when you hold `canGet{ResourceType}Storage` (e.g. `canGetWorkspaceStorage`) on the resource.\n",
        "operationId": "getStorageChart",
        "tags": [
          "Storage"
        ],
        "parameters": [
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "resourceType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "workspace",
                "project",
                "chat",
                "asset"
              ]
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Range start as a UNIX epoch timestamp in milliseconds."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Range end as a UNIX epoch timestamp in milliseconds."
          },
          {
            "name": "aggregationPeriod",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "day",
              "enum": [
                "year",
                "month",
                "week",
                "day"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chart data retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChartDataArray"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "description": "Error generating chart (`errorGeneratingChart`)."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/storage/{resourceType}/{resourceId}": {
      "get": {
        "summary": "Get storage record",
        "description": "Returns the most recent storage record for a resource. Allowed when requesting your own user storage, or when you hold `canGet{ResourceType}Storage` on the resource.\n",
        "operationId": "getStorageRecord",
        "tags": [
          "Storage"
        ],
        "parameters": [
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "resourceType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "workspace",
                "project",
                "chat",
                "asset"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Storage record retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StorageRecord"
                }
              }
            }
          },
          "400": {
            "description": "No storage record exists for the resource (`storageRecordDoesNotExist`)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/tags": {
      "post": {
        "summary": "Create a new tag",
        "description": "Creates a new project-scoped tag. Tags can then be applied to the project's assets, folders, submissions, boards and tasks via their `/tag` endpoints. Requires `canCreateTag` on `ownerResourceId`. Currently only `ownerResourceType: project` is accepted. Tag names must be unique per owner (400 `tagSlugExists`).",
        "tags": [
          "Tags"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "The name of the tag",
                    "example": "High Priority"
                  },
                  "ownerResourceType": {
                    "type": "string",
                    "enum": [
                      "project"
                    ],
                    "description": "The type of resource that will own this tag (only `project` is supported today)",
                    "example": "project"
                  },
                  "ownerResourceId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The ID of the resource that will own this tag",
                    "example": "65af4c7b22ac19c0b1648241"
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "Hex color code for the tag from the predefined color list. When omitted a color is assigned automatically.",
                    "example": "#FF5722"
                  }
                },
                "required": [
                  "name",
                  "ownerResourceType",
                  "ownerResourceId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tag created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tag"
                },
                "examples": {
                  "tagCreated": {
                    "summary": "Successfully created tag",
                    "value": {
                      "id": "65af4c7b22ac19c0b1648555",
                      "name": "High Priority",
                      "slug": "high-priority",
                      "ownerResourceType": "project",
                      "ownerResourceId": "65af4c7b22ac19c0b1648241",
                      "color": "#FF5722",
                      "createdAt": 1678886400000,
                      "updatedAt": 1678886400000
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "nameRequired": {
                    "summary": "Missing tag name",
                    "value": {
                      "type": "validationError",
                      "code": 400,
                      "message": "\"name\" is required"
                    }
                  },
                  "invalidColor": {
                    "summary": "Invalid color format",
                    "value": {
                      "type": "validationError",
                      "code": 400,
                      "message": "\"color\" must be a valid hex color"
                    }
                  },
                  "tagExists": {
                    "summary": "Tag name already exists",
                    "value": {
                      "type": "tagSlugExists",
                      "code": 400,
                      "message": "Tag with this name already exists for this resource."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/UnknownError"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/tags/{ownerResourceType}/{ownerResourceId}": {
      "get": {
        "summary": "Get tags for a resource",
        "description": "Retrieves all tags owned by a specific resource. Requires `canGetTags` on `ownerResourceId`. Currently only `ownerResourceType: project` is accepted.",
        "tags": [
          "Tags"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "ownerResourceType",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "project"
              ]
            },
            "description": "The type of resource to get tags for (only `project` is supported today)",
            "example": "project"
          },
          {
            "name": "ownerResourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the resource to get tags for",
            "example": "65af4c7b22ac19c0b1648241"
          }
        ],
        "responses": {
          "200": {
            "description": "Tags retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tags"
                },
                "examples": {
                  "projectTags": {
                    "summary": "Project tags",
                    "value": [
                      {
                        "id": "65af4c7b22ac19c0b1648555",
                        "name": "High Priority",
                        "slug": "high-priority",
                        "ownerResourceType": "project",
                        "ownerResourceId": "65af4c7b22ac19c0b1648241",
                        "color": "#FF5722",
                        "createdAt": 1678886400000,
                        "updatedAt": 1678886400000
                      },
                      {
                        "id": "65af4c7b22ac19c0b1648556",
                        "name": "In Review",
                        "slug": "in-review",
                        "ownerResourceType": "project",
                        "ownerResourceId": "65af4c7b22ac19c0b1648241",
                        "color": "#2962FF",
                        "createdAt": 1678886500000,
                        "updatedAt": 1678886500000
                      }
                    ]
                  },
                  "emptyTags": {
                    "summary": "No tags found",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidResourceType": {
                    "summary": "Invalid resource type",
                    "value": {
                      "type": "validationError",
                      "code": 400,
                      "message": "\"ownerResourceType\" must be [project]"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/UnknownError"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tags/{tagId}": {
      "put": {
        "summary": "Update a tag",
        "description": "Updates an existing tag's name and/or color. When the name is changed, the slug is automatically regenerated. Requires `canUpdateTag`.",
        "tags": [
          "Tags"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "tagId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the tag to update",
            "example": "65af4c7b22ac19c0b1648555"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "The new name for the tag",
                    "example": "Updated Priority"
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "The new hex color code for the tag from predefined color list",
                    "example": "#2962FF"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tag"
                },
                "examples": {
                  "tagUpdated": {
                    "summary": "Successfully updated tag",
                    "value": {
                      "id": "65af4c7b22ac19c0b1648555",
                      "name": "Updated Priority",
                      "slug": "updated-priority",
                      "ownerResourceType": "project",
                      "ownerResourceId": "65af4c7b22ac19c0b1648241",
                      "color": "#2962FF",
                      "createdAt": 1678886400000,
                      "updatedAt": "2023-03-15T12:10:00.000Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidColor": {
                    "summary": "Invalid color format",
                    "value": {
                      "type": "validationError",
                      "code": 400,
                      "message": "\"color\" must be a valid hex color"
                    }
                  },
                  "nameExists": {
                    "summary": "Tag name already exists",
                    "value": {
                      "type": "tagSlugExists",
                      "code": 400,
                      "message": "Tag with this name already exists for this resource."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/UnknownError"
          }
        },
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a tag",
        "description": "Permanently deletes a tag. This action cannot be undone. Requires `canDeleteTag`.",
        "tags": [
          "Tags"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "tagId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ID of the tag to delete",
            "example": "65af4c7b22ac19c0b1648555"
          }
        ],
        "responses": {
          "200": {
            "description": "Tag deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tag"
                },
                "examples": {
                  "tagDeleted": {
                    "summary": "Successfully deleted tag",
                    "value": {
                      "id": "65af4c7b22ac19c0b1648555",
                      "name": "High Priority",
                      "slug": "high-priority",
                      "ownerResourceType": "project",
                      "ownerResourceId": "65af4c7b22ac19c0b1648241",
                      "color": "#FF5722",
                      "createdAt": 1678886400000,
                      "updatedAt": 1678886400000
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/UnknownError"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/relations": {
      "get": {
        "summary": "List task relations",
        "description": "Returns a page of \"Related To\" entries for a task, populated with chat / message / asset / submission / public-link context and the user who created each relation. Rows whose chat the caller cannot read are filtered out (`totalResults` reflects the filtered page). Requires read access to the task's board.\n",
        "tags": [
          "Task Relations"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Results per page"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of relations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedTaskRelations"
                }
              }
            }
          },
          "403": {
            "description": "Workspace lacks the `boards` capability (`capabilityNotAvailable`) or the user cannot read the task's board"
          }
        },
        "x-request-source": "joi"
      },
      "post": {
        "summary": "Relate a chat or chat message to a task",
        "description": "Creates a relation between a task and either a Chat or a single ChatMessage. The task must be on a board.\nVisibility rules are enforced server-side:\n\n  - Submission and public chats can attach to any task.\n  - Creator chats only attach to tasks on creator-visibility boards.\n  - Reviewer chats only attach to tasks on reviewer-visibility boards.\n\nFor chat messages the visibility is derived from the message's parent chat.\nRequires update access on the task's board plus read access to the chat / message being attached.\n",
        "tags": [
          "Task Relations"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "resourceId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the chat or chat message to relate"
                  },
                  "resourceType": {
                    "type": "string",
                    "enum": [
                      "chat",
                      "chatMessage"
                    ]
                  }
                },
                "required": [
                  "resourceId",
                  "resourceType"
                ],
                "additionalProperties": false,
                "example": {
                  "resourceId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
                  "resourceType": "chatMessage"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created relation (unpopulated)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskRelationRecord"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, invalid resource type (`invalidRelatedResourceType`) or the task is not on a board (`invalidTask`)"
          },
          "403": {
            "description": "Chat visibility is incompatible with the board (`relationVisibilityMismatch`), or the workspace lacks the `boards` capability"
          },
          "404": {
            "description": "Chat / message not found (`relatedResourceNotFound`) or board not found (`boardNotFound`)"
          },
          "409": {
            "description": "Relation already exists (`relationAlreadyExists`)"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/relations/{relationId}": {
      "delete": {
        "summary": "Remove a task relation",
        "description": "Deletes the relation and notifies the board's project channels. Requires update access on the task's board.",
        "tags": [
          "Task Relations"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "relationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Relation removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Relation removed"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Workspace lacks the `boards` capability (`capabilityNotAvailable`) or the user cannot update the task's board"
          },
          "404": {
            "description": "Relation not found (`relatedResourceNotFound`)"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/chats/{chatId}/relations": {
      "get": {
        "summary": "List task relations for a chat",
        "description": "Returns a page of relations whose resource is this chat, populated like `GET /tasks/{taskId}/relations`. Requires read access to the chat.",
        "tags": [
          "Task Relations"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "chatId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Results per page"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of relations referencing this chat",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedTaskRelations"
                }
              }
            }
          },
          "403": {
            "description": "Workspace lacks the `boards` capability (`capabilityNotAvailable`) or the user cannot read the chat"
          },
          "404": {
            "description": "Chat not found"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/chat-messages/{messageId}/relations": {
      "get": {
        "summary": "List task relations for a chat message",
        "description": "Returns a page of relations whose resource is this chat message, populated like `GET /tasks/{taskId}/relations`. Requires read access to the message's chat.",
        "tags": [
          "Task Relations"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Results per page"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of relations referencing this chat message",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedTaskRelations"
                }
              }
            }
          },
          "403": {
            "description": "Workspace lacks the `boards` capability (`capabilityNotAvailable`) or the user cannot read the message"
          },
          "404": {
            "description": "Message not found"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks": {
      "get": {
        "summary": "Get paginated list of tasks assigned to a user.",
        "description": "Returns tasks where `assignedToId` is the authenticated user. Supports index (default) or cursor pagination; the cursor-only and index-only parameters are mutually exclusive with the other `paginate` mode.",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "index",
              "enum": [
                "cursor",
                "index"
              ]
            },
            "description": "Pagination type to use"
          },
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "creatorId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "relatedToId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "visibility",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            }
          },
          {
            "name": "acknowledged",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "acknowledged": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort options"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "maximum": 100,
              "default": 20
            },
            "description": "Limit number of results"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "x-conditionally-required": true
            },
            "description": "Page number (only for index pagination)"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "x-conditionally-required": true
            },
            "description": "Cursor for pagination (only accepted with `paginate=cursor`)."
          },
          {
            "name": "paginateReverse",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, paginates in reverse order (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCounts",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes count information (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeCursorRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the cursor record in results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "startAt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid",
              "x-conditionally-required": true
            },
            "description": "ID of the record to start pagination from (only accepted with `paginate=cursor`)."
          },
          {
            "name": "includeStartAtRecord",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "x-conditionally-required": true
            },
            "description": "If true, includes the startAt record in the results (only accepted with `paginate=cursor`)."
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Get tasks created before this timestamp (only for index pagination)"
          },
          {
            "name": "createdAfter",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "date-time"
                },
                {
                  "type": "string"
                }
              ],
              "default": null,
              "nullable": true,
              "x-conditionally-required": true
            },
            "description": "Get tasks created after this timestamp (only for index pagination)"
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Tasks"
                    },
                    {
                      "$ref": "#/components/schemas/CursorPaginatedTasks"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}": {
      "put": {
        "summary": "Update status of task.",
        "description": "Requires `canUpdateOwnTaskStatus` (the task must be assigned to the caller, or the caller must hold an elevated task permission).",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending",
                      "inProgress",
                      "complete",
                      "closed"
                    ]
                  }
                },
                "required": [
                  "status"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated task status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/TaskNotFound"
          }
        },
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the task."
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a task",
        "description": "Permanently removes a single task. Requires `canRemove{Creator|Reviewer}BoardTask` for\nat least one visibility tier of the task's parent board, plus the `boards` workspace\ncapability. Logs a `taskDeleted` event.\n",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Task deleted (also returned when the task no longer exists)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the task."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/tasks/bulk-create": {
      "post": {
        "summary": "Create several board tasks in one request",
        "description": "Single round-trip replacement for creating tasks one at a time. Every draft is\ncreated in order; per-draft failures are reported inline (`status: 'error'`) and do\nnot abort the batch, so the response is always 200 once validation passes.\n\nAny logged-in user may call this; the per-board `canCreate{Creator|Reviewer}BoardTask`\npermission is checked for each draft (`no_permission`), as is that the board is\nactive and belongs to `projectId` (`board_not_found`). Requires the `boards`\nworkspace capability.\n\nEach created task gets the same side effects as `POST /boards/{boardId}/tasks`\n(event log, notification, `created` task event, topic chat) and, when `tagIds` is\nsupplied, the tags are applied inline. When `announce` is provided and at least one\ntask was created, a single assistant reply carrying every new task is posted in the\nsource chat as a reply to `messageId`; announce failures never fail the request.\n",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "projectId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Project every target board must belong to."
                  },
                  "tasks": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "clientId": {
                          "type": "string",
                          "description": "Caller-chosen ID echoed back in the matching result so drafts can be mapped to outcomes."
                        },
                        "boardId": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "columnId": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Defaults to the board's default column."
                        },
                        "subject": {
                          "type": "string",
                          "maxLength": 500
                        },
                        "description": {
                          "type": "string",
                          "maxLength": 20000,
                          "nullable": true
                        },
                        "assignedToId": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "tagIds": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "format": "uuid"
                          }
                        }
                      },
                      "required": [
                        "clientId",
                        "boardId",
                        "subject"
                      ],
                      "additionalProperties": false
                    },
                    "minItems": 1,
                    "maxItems": 50
                  },
                  "announce": {
                    "type": "object",
                    "properties": {
                      "chatId": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "messageId": {
                        "type": "string",
                        "format": "uuid"
                      }
                    },
                    "required": [
                      "chatId",
                      "messageId"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "projectId",
                  "tasks"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result per submitted draft, in submission order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "clientId": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "created",
                              "error"
                            ]
                          },
                          "task": {
                            "$ref": "#/components/schemas/Task"
                          },
                          "error": {
                            "type": "object",
                            "description": "Present when `status` is `error`.",
                            "properties": {
                              "code": {
                                "type": "string",
                                "description": "`missing_board_id`, `missing_subject`, `board_not_found`, `no_permission`, or the failing ApiError name (e.g. `invalidAssignee`, `reviewerCannotContributeHere`)."
                              },
                              "message": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    },
                    "announce": {
                      "type": "object",
                      "description": "Present only when `announce` was supplied and at least one task was created.",
                      "properties": {
                        "posted": {
                          "type": "boolean"
                        },
                        "messageId": {
                          "type": "string",
                          "format": "uuid",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/CapabilityNotAvailable"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/tasks/acknowledge/{taskId}": {
      "put": {
        "summary": "Toggle acknowledgement of a task.",
        "description": "Flips the task's `acknowledged` flag. Requires `canUpdateOwnTaskStatus`.",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully toggled task acknowledgement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/TaskNotFound"
          }
        },
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the task."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/follow": {
      "put": {
        "summary": "Follow a task",
        "description": "Add the current user to the task's followers list. Idempotent — following again has no effect.",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task with updated followers list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/unfollow": {
      "put": {
        "summary": "Unfollow a task",
        "description": "Remove the current user from the task's followers list.",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task with updated followers list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/events": {
      "get": {
        "summary": "Get task event log",
        "description": "Returns a paginated activity log for a task. Each event includes the actor (public user with avatar) and a structured `detail` object with the context needed to render a human-readable message.\nEvent types: created, addedToBoard, moved, assigned, unassigned, updated, removedFromBoard, linked, unlinked, followed, unfollowed.\n",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "default": {
                "createdAt": -1
              }
            },
            "style": "deepObject",
            "explode": true,
            "description": "Sort by createdAt"
          },
          {
            "name": "eventType",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 50
            },
            "description": "Filter by event type (exact match). Validation accepts any string up to 50 characters; known values are created, addedToBoard, moved, assigned, unassigned, updated, removedFromBoard, linked, unlinked, followed, unfollowed."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of task events.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedTaskEvents"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/tag": {
      "put": {
        "summary": "Tag a task",
        "description": "Add a project tag to a task. Idempotent — tagging again has no effect.",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the project tag to add"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task with updated tags.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/{taskId}/untag": {
      "put": {
        "summary": "Untag a task",
        "description": "Remove a project tag from a task.",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tagId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "tagId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task with updated tags.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/tasks/unacknowledged/{projectId}": {
      "get": {
        "summary": "Get count of unacknowledged tasks for a project.",
        "description": "Counts the authenticated user's unacknowledged tasks in the project, split by visibility.",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved unacknowledged task counts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "creator": {
                      "type": "integer"
                    },
                    "reviewer": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the project."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/tasks/acknowledge-all/{projectId}": {
      "put": {
        "summary": "Acknowledge all of the current user's unacknowledged mention-tasks in a project.",
        "description": "Sets `acknowledged: true` on every unacknowledged mention-task assigned to the authenticated user in the project. Idempotent — already-acknowledged tasks are left as-is and never re-counted. Scoped to the caller's own tasks; other users' tasks are untouched.\n",
        "tags": [
          "Tasks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully acknowledged the user's outstanding mention-tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "acknowledged": {
                      "type": "integer",
                      "description": "Number of tasks acknowledged by this call."
                    },
                    "unacknowledgedCount": {
                      "type": "object",
                      "description": "The user's remaining unacknowledged counts for the project (all zero after this call).",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "creator": {
                          "type": "integer"
                        },
                        "reviewer": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/ProjectNotFound"
          }
        },
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the project."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/users": {
      "get": {
        "summary": "Get logged in user's details.",
        "tags": [
          "Users"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved logged in user details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/users/{userId}": {
      "get": {
        "summary": "Get public information about a user with shared membership.",
        "description": "Returns the public profile of a user who shares at least one workspace/project membership with the caller (or the caller's own public profile).",
        "tags": [
          "Users"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved public user information.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicUser"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/UserNotFound"
          }
        },
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "User ID of the target user."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/users/preferences": {
      "patch": {
        "summary": "Update the logged-in user's preferences.",
        "description": "Patch-merges the supplied fields into the user's `preferences` JSON.\nOmitted keys are left untouched. Used for notification settings, sound\npreferences, hide flags, and onboarding-Todo dismissals. Unknown keys\n(at any level) are rejected with 400.\n\n`dismissed.*` groups per-user UX opt-outs (todos, workspace-setup\nprompts, etc). The `dismissed` object is merged one level deep, so\nwriting one inner key preserves the others. For todos, unknown ids and\nids of Todos that are not dismissible (such as `verifyEmail`) are\nignored.\n",
        "tags": [
          "Users"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hide": {
                    "type": "array",
                    "items": {
                      "enum": [
                        "feedHint",
                        "imagesHint",
                        "theaterHint",
                        "reviewerHint",
                        "deckHint",
                        "videoHint"
                      ]
                    },
                    "description": "In-app hints the user has hidden."
                  },
                  "emailNotifications": {
                    "type": "object",
                    "properties": {
                      "sendEmailNotifications": {
                        "type": "boolean"
                      },
                      "newMention": {
                        "type": "boolean"
                      },
                      "newMentionActiveInterval": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "newFollowing": {
                        "type": "boolean"
                      },
                      "newFollowingActiveInterval": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "newFollowingEmailCooldown": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "newHighlight": {
                        "type": "boolean"
                      },
                      "newHighlightActiveInterval": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "dailySummary": {
                        "type": "boolean"
                      },
                      "publishedAsset": {
                        "type": "boolean"
                      }
                    },
                    "additionalProperties": false
                  },
                  "systemNotifications": {
                    "type": "object",
                    "properties": {
                      "showSystemNotifications": {
                        "type": "boolean"
                      },
                      "showDesktopAppNotifications": {
                        "type": "boolean"
                      },
                      "showWebAppNotifications": {
                        "type": "boolean"
                      },
                      "showMobileAppNotifications": {
                        "type": "boolean"
                      },
                      "mentions": {
                        "type": "boolean"
                      },
                      "alerts": {
                        "type": "boolean"
                      },
                      "privateMessages": {
                        "type": "boolean"
                      }
                    },
                    "additionalProperties": false
                  },
                  "sounds": {
                    "type": "object",
                    "properties": {
                      "playSounds": {
                        "type": "boolean"
                      },
                      "playDesktopAppSounds": {
                        "type": "boolean"
                      },
                      "playWebAppSounds": {
                        "type": "boolean"
                      },
                      "playMobileAppSounds": {
                        "type": "boolean"
                      },
                      "mentions": {
                        "type": "boolean"
                      },
                      "alerts": {
                        "type": "boolean"
                      },
                      "privateMessages": {
                        "type": "boolean"
                      }
                    },
                    "additionalProperties": false
                  },
                  "dismissed": {
                    "type": "object",
                    "properties": {
                      "todos": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "maxLength": 64,
                          "example": "enable2fa"
                        },
                        "description": "Ids of onboarding Todos the user has opted out of (see `GET /users/todos`)."
                      }
                    },
                    "additionalProperties": false,
                    "description": "Per-user UX opt-outs. One-level-deep server-side merge keeps siblings intact when only one key is sent. Only `todos` is currently accepted."
                  },
                  "dailyTips": {
                    "type": "object",
                    "properties": {
                      "disabled": {
                        "type": "boolean",
                        "description": "When true, the daily-tips carousel never shows."
                      },
                      "lastShownDate": {
                        "type": "string",
                        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                        "description": "Local `YYYY-MM-DD` the carousel was last shown; gates the once-per-day rule.",
                        "example": "2026-09-17"
                      }
                    },
                    "additionalProperties": false,
                    "description": "Daily-tips carousel state. Merged one level deep, so writing one key preserves the other.\n"
                  },
                  "featureIntros": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "maxLength": 64
                      }
                    },
                    "description": "Per-project feature-intro tutorials the user has seen (Highlights / Submissions / Public / Boards), keyed by project id. Merged one level deep, so writing one project's list preserves the others.\n"
                  },
                  "dateFormat": {
                    "type": "string",
                    "enum": [
                      "european",
                      "american",
                      "iso"
                    ],
                    "description": "Preferred date display format. Seeded at registration from the country the registration request came from: a month-first country (e.g. the US) yields `american`, everything else `european`. European is the fallback whenever the value is unset or the country cannot be determined. Users can override it (e.g. to `iso`, YYYY-MM-DD) from their profile.\n",
                    "example": "european"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preferences updated. Returns the full user object with merged preferences.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/users/avatar": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Create or update an avatar.",
        "description": "Creates the avatar asset and returns signed upload links for it. Any existing avatar asset is marked `pendingDelete`. POST and PUT are identical.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum2"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Avatar asset and upload links created/updated successfully. The upload-link entry is returned at the top level together with the updated `user`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AssetAndSignedLink"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "user": {
                          "$ref": "#/components/schemas/User"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "`assetUploadError` — the avatar asset could not be created."
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      },
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Create or update an avatar.",
        "description": "Creates the avatar asset and returns signed upload links for it. Any existing avatar asset is marked `pendingDelete`. POST and PUT are identical.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum2"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Avatar asset and upload links created/updated successfully. The upload-link entry is returned at the top level together with the updated `user`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AssetAndSignedLink"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "user": {
                          "$ref": "#/components/schemas/User"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "description": "`assetUploadError` — the avatar asset could not be created."
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      }
    },
    "/version/commit": {
      "get": {
        "summary": "Get the latest git commit hash",
        "tags": [
          "Version"
        ],
        "description": "Returns the latest git commit hash and build timestamp of the deployed application. Public — no authentication required. The commit is injected during the build process, so accurate values are only available on deployed builds.\n",
        "responses": {
          "200": {
            "description": "Successfully retrieved the latest git commit hash.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "buildCommit": {
                      "type": "string",
                      "example": "ab12cd34ef56gh78ij90kl [timeStamp]",
                      "description": "The git commit hash of the current build."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/{workspaceId}/webhooks": {
      "post": {
        "summary": "Create a webhook subscription",
        "description": "Registers a delivery URL for the given events and returns the new subscription together with its `signingSecret` — the only time the secret is returned other than on rotate. The URL must be `http(s)`, ≤ 2048 chars, and must not point at a private / loopback / link-local address (literal IPs are rejected outright; hostnames are DNS-resolved and checked). Deployments that require HTTPS reject `http://` URLs except for `localhost`.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Customer-facing label. Whitespace-only names are rejected."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048,
                    "description": "Delivery endpoint. `http` or `https`; public hosts only."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "task.created",
                        "task.updated",
                        "task.deleted",
                        "chat.message.created",
                        "asset.published",
                        "webhook.test"
                      ]
                    },
                    "minItems": 1,
                    "description": "Wire event names to subscribe to. Duplicates are removed."
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "Optional ISO timestamp in the future. Null / omitted means no expiry."
                  }
                },
                "required": [
                  "name",
                  "url",
                  "events"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created. Store `signingSecret` now — it cannot be retrieved later.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscription": {
                      "$ref": "#/components/schemas/WebhookSubscription"
                    },
                    "signingSecret": {
                      "type": "string",
                      "description": "HMAC signing secret (plaintext). Returned once."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or one of `webhookUrlInvalid`, `webhookUrlInsecure`, `webhookUrlBlocked` (resolves to a private address), `webhookUrlUnresolvable`, `webhookNameRequired`, `webhookEventsInvalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Caller lacks `canManageWebhooks`, or the workspace has reached the cap (`webhookLimitReached`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          }
        ],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "List the workspace's webhook subscriptions",
        "description": "Newest first. Secrets are never included.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subscriptions in the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookSubscription"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/webhooks/{webhookId}": {
      "get": {
        "summary": "Get a webhook subscription",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription (sanitised).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscription"
                }
              }
            }
          },
          "404": {
            "description": "No subscription with this id in this workspace."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook subscription ID."
          }
        ],
        "x-request-source": "joi"
      },
      "patch": {
        "summary": "Update a webhook subscription",
        "description": "Partial update; at least one field is required. `status` accepts only the admin-controllable transitions `active` and `paused` — setting `active` on a `failedOut` subscription re-enables it and clears `failedOutAt` / `failedOutReason`. Pass `expiresAt: null` to clear an expiry. URL and event changes go through the same checks as create.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "task.created",
                        "task.updated",
                        "task.deleted",
                        "chat.message.created",
                        "asset.published",
                        "webhook.test"
                      ]
                    },
                    "minItems": 1
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ]
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "Future ISO timestamp to set / extend, or null to clear."
                  }
                },
                "additionalProperties": false,
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscription"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or `webhookUpdateEmpty`, `webhookNameRequired`, `webhookStatusInvalid`, `webhookEventsInvalid`, `webhookUrlInvalid`, `webhookUrlInsecure`, `webhookUrlBlocked`, `webhookUrlUnresolvable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No subscription with this id in this workspace."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook subscription ID."
          }
        ],
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Delete a webhook subscription",
        "description": "Permanently removes the subscription and its delivery attempts.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "404": {
            "description": "No subscription with this id in this workspace."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook subscription ID."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/webhooks/{webhookId}/rotate-secret": {
      "post": {
        "summary": "Rotate the signing secret",
        "description": "Generates a new HMAC signing secret and returns it once. Deliveries signed after this call use the new secret immediately; update the receiver's verification config first if you need zero-downtime rotation.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "New secret issued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscription": {
                      "$ref": "#/components/schemas/WebhookSubscription"
                    },
                    "signingSecret": {
                      "type": "string",
                      "description": "The new plaintext signing secret. Returned once."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No subscription with this id in this workspace."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook subscription ID."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/webhooks/{webhookId}/test": {
      "post": {
        "summary": "Queue a test delivery",
        "description": "Fires a synthetic `webhook.test` event through the normal delivery pipeline. The subscription does not need `webhook.test` in its `events` list. The response only confirms the event was queued — inspect the delivery log (`GET .../deliveries`) for the outcome.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "202": {
            "description": "Test delivery queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string",
                      "example": "Test webhook delivery queued. Check the delivery log in a moment."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No subscription with this id in this workspace."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook subscription ID."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/webhooks/{webhookId}/deliveries": {
      "get": {
        "summary": "List delivery attempts",
        "description": "Cursor-paginated delivery log, newest first. Each retry of a notification is its own attempt. Pass the returned `nextCursor` back as `cursor` to fetch the next page.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook subscription ID."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor from a previous response's `nextCursor` (an ISO `createdAt` timestamp; only older attempts are returned)."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "inflight",
                "succeeded",
                "failed",
                "dlq"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of attempts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookAttempt"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true,
                      "description": "Cursor for the next page, or null when there are no more attempts."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No subscription with this id in this workspace."
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/webhooks/{webhookId}/deliveries/{attemptId}/replay": {
      "post": {
        "summary": "Replay a delivery",
        "description": "Re-queues the attempt's notification for delivery. A NEW attempt is created (the original is kept for the audit trail) and the notification is queued for delivery again.",
        "tags": [
          "Webhook Subscriptions"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "202": {
            "description": "Replay queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attempt": {
                      "$ref": "#/components/schemas/WebhookAttempt"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No subscription with this id in this workspace, or no attempt with this id on the subscription."
          }
        },
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Webhook subscription ID."
          },
          {
            "name": "attemptId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "ID of an existing attempt on this subscription."
          }
        ],
        "x-request-source": "joi"
      }
    },
    "/workspaces": {
      "post": {
        "summary": "Create a new workspace",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 350
                  },
                  "logo": {
                    "type": "string",
                    "description": "URL to logo"
                  },
                  "icon": {
                    "type": "string",
                    "description": "URL to icon"
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "Hex colour code from the platform's approved palette (e.g. `#37474F`).",
                    "example": "#37474F"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Workspace created successfully. The response is the workspace row with `roles: ['workspaceOwner']` stamped on for the caller. `capabilities` is not included on create (there is no subscription yet) — fetch the workspace to get it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceWithRoles"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "parameters": [],
        "x-request-source": "joi"
      },
      "get": {
        "summary": "Get all workspaces for the authenticated user",
        "description": "Returns every workspace the caller holds a membership in, each stamped with the caller's `roles` and the workspace's `capabilities`. Sort with the `sort` query parameter (deep-object style, e.g. `?sort[name]=1&sort[createdAt]=-1`). A `sort` object in the request body is still accepted as a deprecated alias for one release.",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "description": "Sort directions per field (1 ascending, -1 descending)."
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved user's workspaces.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WorkspaceWithRoles"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sort": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "name": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "createdAt": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      },
                      "updatedAt": {
                        "type": "number",
                        "enum": [
                          1,
                          -1
                        ]
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}": {
      "get": {
        "summary": "Get details of a specific workspace",
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          }
        ],
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved workspace details, including the caller's `roles` and the workspace's `capabilities`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceWithRoles"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "put": {
        "summary": "Update details of a specific workspace",
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          }
        ],
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 350
                  },
                  "updateSlug": {
                    "type": "boolean",
                    "default": false,
                    "description": "Determines if the slug will be regenerated or not. Meaning you could change the name but keep the old slug as it may be used in static links."
                  },
                  "color": {
                    "type": "string",
                    "enum": [
                      "#37474F",
                      "#FF5722",
                      "#2962FF",
                      "#33691E",
                      "#00796B",
                      "#455A64",
                      "#2979FF",
                      "#827717",
                      "#7986CB",
                      "#8E24AA",
                      "#9575CD",
                      "#BF360C",
                      "#01579B",
                      "#EF6C00",
                      "#AA00FF",
                      "#F44336",
                      "#7C4DFF",
                      "#E65100",
                      "#8D6E63",
                      "#283593",
                      "#607D8B",
                      "#009688",
                      "#FF5252",
                      "#03A9F4",
                      "#C2185B",
                      "#00ACC1",
                      "#E91E63",
                      "#5D4037",
                      "#78909C",
                      "#1E88E5",
                      "#D500F9",
                      "#7E57C2",
                      "#5C6BC0",
                      "#558B2F",
                      "#2E7D32",
                      "#F50057",
                      "#004D40",
                      "#0D47A1",
                      "#C51162",
                      "#D50000",
                      "#6200EA",
                      "#00BCD4",
                      "#0277BD"
                    ],
                    "description": "Hex colour code from the platform's approved palette (e.g. `#37474F`).",
                    "example": "#37474F"
                  }
                },
                "additionalProperties": false,
                "description": "All fields optional. `logo` / `icon` are NOT accepted here — use the `/logo` and `/icon` endpoints."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully updated workspace details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workspace"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "delete": {
        "summary": "Change a workspace's status to 'pendingDelete' for later cleanup.",
        "description": "Requires `canDeleteWorkspace`, which only the workspace OWNER holds — not `canUpdateWorkspace`, which admins also hold. Deleting the workspace is the one way an admin could take it away from its owner, so it costs the owner-only right.\n",
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          }
        ],
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Workspace successfully marked for deletion. Returns the updated workspace (`status: pendingDelete`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workspace"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/projects": {
      "get": {
        "summary": "Get projects within a specific workspace",
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "name": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "createdAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                },
                "updatedAt": {
                  "type": "number",
                  "enum": [
                    1,
                    -1
                  ]
                }
              },
              "additionalProperties": false,
              "description": "Sort directions per field (1 ascending, -1 descending)."
            },
            "style": "deepObject",
            "explode": true
          }
        ],
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved projects within the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Projects"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/logo": {
      "post": {
        "deprecated": true,
        "tags": [
          "Workspaces"
        ],
        "summary": "Create or update workspace logo. (deprecated alias; use PUT)",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the workspace"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum3"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Workspace logo asset and upload links created/updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceImageUploadResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "put": {
        "tags": [
          "Workspaces"
        ],
        "summary": "Create or update workspace logo.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the workspace"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum3"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Workspace logo asset and upload links created/updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceImageUploadResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/icon": {
      "post": {
        "deprecated": true,
        "tags": [
          "Workspaces"
        ],
        "summary": "Create or update workspace icon. (deprecated alias; use PUT)",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the workspace"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum3"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Workspace icon asset and upload links created/updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceImageUploadResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      },
      "put": {
        "tags": [
          "Workspaces"
        ],
        "summary": "Create or update workspace icon.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique ID of the workspace"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SharedIdNameChecksum3"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Workspace icon asset and upload links created/updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceImageUploadResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      }
    },
    "/workspaces/{workspaceId}/settings/{name}": {
      "put": {
        "tags": [
          "Workspaces"
        ],
        "summary": "Update a single workspace setting.",
        "description": "Sets one key of the workspace's `settings` JSON column. The `name` path parameter selects the setting and determines the type `value` must have (booleans for the toggles, bounded integers for the credit budgets, strings for the pre-prompts, `{ toolName: boolean|null }` maps for `aiChatTools` / `aiAssistTools`). Requires `canManageWorkspaceSettings` on the workspace.\n\nSetting-specific behaviour:\n- `autoTopUpEnabled: true` is refused with `autoTopUpRequiresPriorTopUp`\n  until the workspace has made at least one manual credit top-up.\n\n- Flipping `boardsEnabled` true→false schedules every board in the\n  workspace for deletion after a 48h grace window; flipping it back\n  within the window restores them.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workspaceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique identifier of the workspace."
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "aiAddOnEnabled",
                "allowAiFeatures",
                "aiPolishEnabled",
                "aiComposeEnabled",
                "aiChatEnabled",
                "aiAssistEnabled",
                "aiAssistFollowUpWindow",
                "aiTaskGenerationEnabled",
                "aiImageRevisionEnabled",
                "aiPolishAllowReviewer",
                "aiComposeAllowReviewer",
                "aiChatAllowReviewer",
                "aiAssistAllowReviewer",
                "aiTaskGenerationAllowReviewer",
                "aiImageRevisionAllowReviewer",
                "aiCustomPreprompt",
                "aiTaskGenerationPreprompt",
                "dailyUserAiSpendBudget",
                "autoTopUpEnabled",
                "autoTopUpThresholdCredits",
                "autoTopUpAmountCredits",
                "autoTopUpMonthlyLimitCredits",
                "aiChatTools",
                "aiAssistTools",
                "boardsEnabled",
                "convosEnabled"
              ]
            },
            "description": "The setting to update."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "value": {
                    "description": "New value. Type depends on `name`: `*Enabled` / `allowAiFeatures` / `*AllowReviewer` — boolean; `aiAssistFollowUpWindow` — integer 0..30; `aiCustomPreprompt` / `aiTaskGenerationPreprompt` — string ≤ 8000 chars ('' clears); `dailyUserAiSpendBudget` — integer 0..1000000 (0 disables the cap); `autoTopUp*Credits` — integer ≥ 0; `aiChatTools` / `aiAssistTools` — object mapping `^[a-z][a-z0-9_]*$` tool names to boolean or null."
                  }
                },
                "required": [
                  "value"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Setting updated. Returns the full updated workspace row (including `settings`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workspace"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (unknown `name`, or `value` has the wrong type/range for it — `validationError`), `workspaceInvalidSetting`, or `autoTopUpRequiresPriorTopUp`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/WorkspaceNotFound"
          }
        },
        "x-request-source": "joi"
      }
    }
  },
  "components": {
    "schemas": {
      "BaseAnnotation": {
        "type": "object",
        "properties": {
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "default": 0,
            "description": "Video frame number (for video annotations)"
          },
          "timestamp": {
            "type": "number",
            "description": "Legacy single timestamp in milliseconds (for backward compatibility)"
          },
          "startTimestamp": {
            "type": "number",
            "description": "Range start timestamp or single point timestamp in milliseconds"
          },
          "endTimestamp": {
            "type": "number",
            "description": "Range end timestamp in milliseconds (optional, creates range if provided)"
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "default": 1,
            "description": "Horizontal scaling factor"
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "default": 1,
            "description": "Vertical scaling factor"
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "default": 0,
            "description": "Rotation angle in degrees"
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "default": 0,
            "description": "X-axis skewing in degrees"
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "default": 0,
            "description": "Y-axis skewing in degrees"
          },
          "flipX": {
            "type": "boolean",
            "default": false,
            "description": "Horizontal flip"
          },
          "flipY": {
            "type": "boolean",
            "default": false,
            "description": "Vertical flip"
          },
          "originX": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "left",
            "description": "Transform origin X"
          },
          "originY": {
            "type": "string",
            "enum": [
              "top",
              "center",
              "bottom"
            ],
            "default": "top",
            "description": "Transform origin Y"
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 1,
            "description": "Object opacity"
          },
          "visible": {
            "type": "boolean",
            "default": true,
            "description": "Object visibility"
          },
          "shadow": {
            "type": "object",
            "properties": {
              "color": {
                "type": "string",
                "default": "rgba(0,0,0,0.3)",
                "description": "Shadow color"
              },
              "blur": {
                "type": "number",
                "minimum": 0,
                "maximum": 50,
                "default": 5,
                "description": "Shadow blur radius in pixels"
              },
              "offsetX": {
                "type": "number",
                "minimum": -10,
                "maximum": 10,
                "default": 0.2,
                "description": "Shadow X offset as percentage of media width"
              },
              "offsetY": {
                "type": "number",
                "minimum": -10,
                "maximum": 10,
                "default": 0.2,
                "description": "Shadow Y offset as percentage of media height"
              }
            }
          },
          "strokeLineCap": {
            "type": "string",
            "enum": [
              "butt",
              "round",
              "square"
            ],
            "default": "butt",
            "description": "Line cap style"
          },
          "strokeLineJoin": {
            "type": "string",
            "enum": [
              "miter",
              "round",
              "bevel"
            ],
            "default": "miter",
            "description": "Line join style"
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "default": 4,
            "description": "Miter limit for line joins"
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "enum": [
              "nonzero",
              "evenodd"
            ],
            "default": "nonzero",
            "description": "Fill rule for complex shapes"
          },
          "nestedAnnotations": {
            "type": "array",
            "maxItems": 10,
            "description": "Optional array of nested annotations (spatial annotations within this annotation's context). IMPORTANT - Nested annotations can ONLY be used when the parent annotation has a timestamp (timestamp, startTimestamp, or endTimestamp). Nested annotations inherit the parent's timestamp and cannot have their own timestamps or nested annotations.",
            "items": {
              "$ref": "#/components/schemas/NestedAnnotation"
            }
          }
        }
      },
      "PercentageCoordinates": {
        "type": "object",
        "properties": {
          "x": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X coordinate as percentage (0-100)"
          },
          "y": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y coordinate as percentage (0-100)"
          }
        },
        "required": [
          "x",
          "y"
        ]
      },
      "NestedAnnotation": {
        "oneOf": [
          {
            "type": "object",
            "description": "Nested dot annotation",
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAnnotation"
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "dot"
                    ]
                  },
                  "coordinates": {
                    "$ref": "#/components/schemas/PercentageCoordinates"
                  },
                  "radius": {
                    "type": "number",
                    "minimum": 5,
                    "maximum": 100,
                    "default": 10
                  }
                },
                "required": [
                  "type",
                  "coordinates"
                ]
              }
            ]
          },
          {
            "type": "object",
            "description": "Nested shape annotation",
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAnnotation"
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "rectangle",
                      "circle",
                      "triangle",
                      "arrow",
                      "line"
                    ]
                  },
                  "coordinates": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/PercentageCoordinates"
                    },
                    "minItems": 2,
                    "maxItems": 50
                  },
                  "strokeColor": {
                    "type": "string",
                    "default": "#000000"
                  },
                  "fillColor": {
                    "type": "string"
                  },
                  "strokeWidth": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 50,
                    "default": 2
                  }
                },
                "required": [
                  "type",
                  "coordinates"
                ]
              }
            ]
          },
          {
            "type": "object",
            "description": "Nested text annotation",
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAnnotation"
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "text"
                    ]
                  },
                  "coordinates": {
                    "$ref": "#/components/schemas/PercentageCoordinates"
                  },
                  "content": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "fontSize": {
                    "type": "number",
                    "minimum": 8,
                    "maximum": 72,
                    "default": 16
                  },
                  "fontFamily": {
                    "type": "string",
                    "enum": [
                      "Arial",
                      "Helvetica",
                      "Times New Roman",
                      "Courier New",
                      "Georgia",
                      "Verdana"
                    ],
                    "default": "Arial"
                  },
                  "fontWeight": {
                    "type": "string",
                    "enum": [
                      "normal",
                      "bold"
                    ],
                    "default": "normal"
                  },
                  "fontStyle": {
                    "type": "string",
                    "enum": [
                      "normal",
                      "italic"
                    ],
                    "default": "normal"
                  },
                  "textColor": {
                    "type": "string",
                    "default": "#000000"
                  }
                },
                "required": [
                  "type",
                  "coordinates",
                  "content"
                ]
              }
            ]
          },
          {
            "type": "object",
            "description": "Nested path annotation",
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAnnotation"
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "path"
                    ]
                  },
                  "pathData": {
                    "type": "string",
                    "maxLength": 10000
                  },
                  "strokeColor": {
                    "type": "string",
                    "default": "#000000"
                  },
                  "strokeWidth": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 50,
                    "default": 2
                  }
                },
                "required": [
                  "type",
                  "pathData"
                ]
              }
            ]
          }
        ]
      },
      "DotAnnotation": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BaseAnnotation"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "dot"
                ]
              },
              "coordinates": {
                "$ref": "#/components/schemas/PercentageCoordinates"
              },
              "radius": {
                "type": "number",
                "default": 10,
                "minimum": 5,
                "maximum": 100
              }
            },
            "required": [
              "type",
              "coordinates"
            ]
          }
        ]
      },
      "FrameCommentAnnotation": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BaseAnnotation"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "frameComment"
                ]
              }
            },
            "required": [
              "type"
            ]
          }
        ]
      },
      "DotValidation": {
        "$ref": "#/components/schemas/DotAnnotation"
      },
      "FrameCommentValidation": {
        "$ref": "#/components/schemas/FrameCommentAnnotation"
      },
      "AssetReferences": {
        "type": "object",
        "description": "Every location one asset is referenced. The PRIMARY file system is the asset's home; SECONDARY references are the reviewer tree, submissions and public releases. Renaming the asset fans out to all of them, and deleting the last primary reference removes all of them.\n",
        "properties": {
          "assetId": {
            "type": "string",
            "format": "uuid"
          },
          "assetName": {
            "type": "string"
          },
          "primaryRoot": {
            "type": "string",
            "description": "Root path of the asset's home file system."
          },
          "primary": {
            "type": "array",
            "description": "Where the asset's own file system holds it.",
            "items": {
              "type": "object",
              "properties": {
                "fileSystemId": {
                  "type": "string",
                  "format": "uuid"
                },
                "path": {
                  "type": "string"
                },
                "itemPath": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "secondary": {
            "type": "object",
            "description": "References outside the home file system, keyed by collection. All four keys are always present; a collection with no references is an empty array.\n",
            "properties": {
              "reviewer": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AssetReference"
                }
              },
              "submission": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AssetReference"
                }
              },
              "public": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AssetReference"
                }
              },
              "other": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AssetReference"
                }
              }
            }
          },
          "counts": {
            "type": "object",
            "description": "Count per collection. Only collections with at least one reference appear."
          },
          "secondaryCount": {
            "type": "integer",
            "description": "Total secondary references. Zero means the asset lives only in its own folder."
          }
        },
        "required": [
          "assetId",
          "assetName",
          "primaryRoot",
          "primary",
          "secondary",
          "counts",
          "secondaryCount"
        ]
      },
      "AssetReference": {
        "type": "object",
        "description": "One location outside the asset's home file system.",
        "properties": {
          "fileSystemId": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "reviewer",
              "submission",
              "public",
              "other"
            ]
          },
          "path": {
            "type": "string"
          },
          "itemPath": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "description": "Human-readable location, e.g. \"Submission / Round 2\"."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Tasks": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          }
        ]
      },
      "Task": {
        "type": "object",
        "description": "A task created from a chat message mention or on a project board. Timestamps are ISO 8601 strings. The `creator`, `project`, `origin`, `relatedTo`, `assignedTo` and `inheritance` keys are aliases of the matching `*Id` fields kept for backward compatibility.",
        "properties": {
          "id": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The unique identifier of the task"
          },
          "creatorId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The creators user ID."
          },
          "creator": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Alias of creatorId."
          },
          "projectId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The ID of the project the task belongs to."
          },
          "project": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Alias of projectId."
          },
          "assignedToId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The user the task is assigned to. Omitted when unassigned."
          },
          "assignedTo": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Alias of assignedToId. Omitted when unassigned."
          },
          "originId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The ID of the resource where the task was created. Null for tasks created directly on a board.",
            "nullable": true
          },
          "origin": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Alias of originId.",
            "nullable": true
          },
          "originType": {
            "type": "string",
            "enum": [
              "chatMessage"
            ],
            "nullable": true,
            "description": "The type of resource the task originated from. Null for tasks created directly on a board."
          },
          "relatedToId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The ID of the resource the task is related to. For example of the task was related to a particular asset this would be the asset ID."
          },
          "relatedTo": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Alias of relatedToId."
          },
          "relatedToType": {
            "type": "string",
            "enum": [
              "project",
              "asset",
              "chatMember",
              "chatSubmission"
            ],
            "description": "The type of resource the task is related to."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "inProgress",
              "complete",
              "closed"
            ],
            "default": "pending",
            "description": "Status of the task."
          },
          "acknowledged": {
            "type": "boolean",
            "default": false,
            "description": "Whether the mentioned user has acknowledged the task."
          },
          "visibility": {
            "type": "string",
            "enum": [
              "creator",
              "reviewer",
              "user"
            ],
            "nullable": true,
            "description": "Which side of the project the task is visible to."
          },
          "mentions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UUID"
            },
            "description": "IDs of users mentioned by the task."
          },
          "followers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UUID"
            },
            "description": "IDs of users following the task."
          },
          "subject": {
            "type": "string",
            "description": "Short description of the task."
          },
          "body": {
            "type": "string",
            "nullable": true,
            "description": "Body of the task."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Rich description for board tasks."
          },
          "boardId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The board the task belongs to. Null for legacy mention tasks not on a board.",
            "nullable": true
          },
          "columnId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "The board column the task is in.",
            "nullable": true
          },
          "sortOrder": {
            "type": "integer",
            "default": 0,
            "description": "Position within the column."
          },
          "taskNumber": {
            "type": "integer",
            "nullable": true,
            "description": "Per-project auto-incrementing task number (#1,"
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UUID"
            },
            "description": "Project-scoped tag IDs applied to the task."
          },
          "inheritanceId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "ID of the inheritance record. Omitted when not set."
          },
          "inheritance": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Alias of inheritanceId. Omitted when not set."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "creatorId",
          "projectId",
          "relatedToId",
          "relatedToType",
          "status",
          "subject"
        ]
      },
      "LastSeen": {
        "type": "object",
        "description": "An object formated {[channel]:timestamp} showing the last time a user has viewed a channel.",
        "example": "{/workspace/[workspaceId]:[timestamp]}",
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "lastSeen": {
            "type": "integer",
            "format": "int64"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "example": 1678886400000
          }
        }
      },
      "Notification": {
        "type": "object",
        "description": "A platform notification. `createdAt` is an ISO 8601 string; notifications have no `updatedAt`. Internal push / webhook delivery fields are never returned.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the notification"
          },
          "_id": {
            "type": "string",
            "format": "uuid",
            "description": "Deprecated alias of `id` kept for backward compatibility."
          },
          "type": {
            "type": "string",
            "description": "The type of notification as defined in config/notifications."
          },
          "initiatorId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The ID of the user who initiated the notification. Null for system notifications."
          },
          "initiatorType": {
            "type": "string",
            "description": "Whether a user or the system initiated the notification.",
            "enum": [
              "user",
              "system"
            ],
            "default": "user"
          },
          "initiator": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ],
            "description": "The public user information for the initiator of the notification. Not present for system notifications."
          },
          "resourceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The ID of the resource the notification is about."
          },
          "resourceType": {
            "type": "string",
            "nullable": true,
            "description": "The type of resource the notification is about.",
            "enum": [
              "workspace",
              "project",
              "asset",
              "chat",
              "chatMember",
              "chatMessage",
              "chatSubmission",
              "convo",
              "user",
              "folder",
              "invite",
              "membership",
              "task",
              "board",
              "lastSeen",
              "summary",
              "subscription",
              "tag",
              "public",
              "settings"
            ]
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The channels the notification was emitted to (see the Websocket documentation)."
          },
          "tokens": {
            "type": "object",
            "description": "Tokens as defined by the notifications template in config/notifications"
          },
          "changes": {
            "type": "object",
            "nullable": true,
            "description": "The changes that occurred to the resource. Not all keys may be present.",
            "properties": {
              "create": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "resourceId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "resourceType": {
                      "type": "string"
                    },
                    "resource": {
                      "type": "object",
                      "description": "The resource that was created."
                    }
                  }
                }
              },
              "update": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "resourceId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "resourceType": {
                      "type": "string"
                    },
                    "oldResource": {
                      "type": "object",
                      "description": "The resource before the update. ( Optional )"
                    },
                    "resource": {
                      "type": "object",
                      "description": "The resource that was updated."
                    }
                  }
                }
              },
              "delete": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "resourceId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "resourceType": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "scheduledSendAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When a delayed push for this notification is scheduled to send."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp when the notification was created"
          }
        },
        "required": [
          "id",
          "type",
          "tokens",
          "initiatorType",
          "channels"
        ]
      },
      "UUID": {
        "type": "string",
        "format": "uuid",
        "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
      },
      "AssetAndSignedLinks": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/AssetAndSignedLink"
        }
      },
      "AssetAndSignedLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "example": 1
          },
          "name": {
            "type": "string",
            "example": "file1.png"
          },
          "hash": {
            "type": "string",
            "example": "d9729feb74992cc3482b350163a1a010"
          },
          "type": {
            "type": "string",
            "example": "image"
          },
          "asset": {
            "$ref": "#/components/schemas/Asset"
          },
          "signedUrlData": {
            "$ref": "#/components/schemas/SignedUrlData"
          },
          "status": {
            "type": "string",
            "example": "success"
          }
        }
      },
      "RepairAssetAndSignedLinks": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/RepairAssetAndSignedLink"
        }
      },
      "RepairAssetAndSignedLink": {
        "type": "object",
        "properties": {
          "assetId": {
            "$ref": "#/components/schemas/UUID"
          },
          "status": {
            "type": "string",
            "example": "success"
          },
          "asset": {
            "$ref": "#/components/schemas/Asset"
          },
          "uploadChunkSizeInBytes": {
            "type": "integer",
            "format": "int64",
            "example": 209715200
          },
          "signedUrlData": {
            "$ref": "#/components/schemas/SignedUrlData"
          }
        }
      },
      "SignedUrlData": {
        "type": "object",
        "properties": {
          "fileName": {
            "type": "string",
            "example": "file1.png"
          },
          "ownerAssetId": {
            "type": "string",
            "example": "65af4c7b22ac19c0b1648241"
          },
          "key": {
            "type": "string",
            "example": "/workspaces/ws-65af4c7b22ac19c0b1648226/projects/pr..."
          },
          "urls": {
            "type": "array",
            "items": {
              "format": "uri",
              "example": "https://s3.eu-west-1.amazonaws.com/files-test.nurama.io..."
            }
          },
          "mimeType": {
            "type": "string",
            "example": "image/png"
          },
          "expires": {
            "type": "integer",
            "format": "int64",
            "example": 1705988395
          },
          "status": {
            "type": "string",
            "example": "success"
          }
        }
      },
      "DownloadSignedUrlData": {
        "type": "object",
        "properties": {
          "fileName": {
            "type": "string",
            "example": "file1.png"
          },
          "ownerAssetId": {
            "type": "string",
            "example": "65af4c7b22ac19c0b1648241"
          },
          "key": {
            "type": "string",
            "example": "/workspaces/ws-65af4c7b22ac19c0b1648226/projects/pr..."
          },
          "url": {
            "type": "string",
            "example": "https://s3.eu-west-1.amazonaws.com/files-test.nurama.io..."
          },
          "mimeType": {
            "type": "string",
            "example": "image/png"
          },
          "expires": {
            "type": "integer",
            "format": "int64",
            "example": 1705988395
          },
          "status": {
            "type": "string",
            "example": "success"
          }
        }
      },
      "Memberships": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Membership"
        }
      },
      "Membership": {
        "type": "object",
        "description": "A user's membership of a workspace or project. Timestamps are ISO 8601 strings.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "workspace",
              "project"
            ],
            "example": "workspace"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "workspaceOwner",
                "workspaceAdmin",
                "workspaceMember",
                "workspaceChatMember",
                "projectOwner",
                "projectAdmin",
                "creator",
                "reviewer",
                "reviewerAdmin",
                "reviewerBoardManager"
              ]
            },
            "example": [
              "workspaceOwner"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "resource": {
            "description": "Populated resource, when the request populates it.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Workspace"
              },
              {
                "$ref": "#/components/schemas/Project"
              }
            ]
          },
          "user": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ],
            "description": "Populated public user, when the request populates it."
          }
        },
        "required": [
          "id",
          "userId",
          "resourceId",
          "resourceType",
          "roles"
        ]
      },
      "MembershipReport": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "$ref": "#/components/schemas/PublicUser"
                    },
                    "resources": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "resourceId": {
                            "type": "string",
                            "example": "6733736868949390a715fc63"
                          },
                          "resourceType": {
                            "type": "string",
                            "example": "project"
                          },
                          "name": {
                            "type": "string",
                            "example": "Orn Group"
                          },
                          "roles": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "example": "reviewer, creator, projectOwner ect."
                            }
                          }
                        }
                      }
                    },
                    "membershipRecords": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "6733736868949390a715fc63"
                          },
                          "resourceId": {
                            "type": "string",
                            "example": "6733736868949390a715fc63"
                          },
                          "resourceType": {
                            "type": "string",
                            "example": "project"
                          },
                          "roles": {
                            "items": {
                              "type": "string",
                              "example": "creator"
                            }
                          }
                        }
                      }
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "example": "2024-11-12T15:25:28.620Z"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "example": "2024-11-12T15:25:28.620Z"
                    },
                    "userId": {
                      "type": "string",
                      "example": "6733736768949390a715fac4"
                    },
                    "roles": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "example": "reviewer"
                      }
                    },
                    "isBillable": {
                      "type": "boolean",
                      "example": false
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "Mentionable": {
        "type": "array",
        "items": {
          "allOf": [
            {
              "$ref": "#/components/schemas/PublicUser"
            },
            {
              "properties": {
                "roles": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "example": "reviewer"
                  }
                }
              }
            }
          ]
        }
      },
      "Invites": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Invite"
        }
      },
      "Invite": {
        "type": "object",
        "description": "An invitation to join a workspace or project. `createdAt` and `updatedAt` are Unix millisecond timestamps; `expires` is an ISO 8601 string. The invite's `id` doubles as the invite token used in accept links.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0192a3b4-5c6d-7e8f-9a0b-1c2d3e4f5a6b"
          },
          "inviterId": {
            "type": "string",
            "format": "uuid"
          },
          "inviteeEmail": {
            "type": "string",
            "format": "email",
            "example": "bernice_koss@hotmail.com"
          },
          "inviteeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Only set once the invitee has an account on the platform."
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "workspace",
              "project"
            ],
            "example": "workspace"
          },
          "role": {
            "type": "string",
            "enum": [
              "workspaceAdmin",
              "projectAdmin",
              "creator",
              "reviewer",
              "reviewerAdmin"
            ],
            "example": "workspaceAdmin"
          },
          "additionalRoles": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "workspaceChatMember",
                "workspaceAdmin"
              ]
            },
            "description": "Additive roles granted alongside `role` when the invite is accepted."
          },
          "expires": {
            "type": "string",
            "format": "date-time",
            "example": "2023-12-15T12:12:34.784Z"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "canceled",
              "accepted"
            ],
            "example": "active"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "example": 1704461497862
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "example": 1704461497862
          },
          "resource": {
            "description": "The invited-to resource when populated.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Workspace"
              },
              {
                "$ref": "#/components/schemas/Project"
              }
            ]
          },
          "inviter": {
            "description": "The inviter's user ID, or the populated public user when the request populates it.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ]
          },
          "invitee": {
            "nullable": true,
            "description": "The invitee's user ID (null until they have an account), or the populated public user when the request populates it.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ]
          }
        },
        "required": [
          "id",
          "inviterId",
          "inviteeEmail",
          "resourceId",
          "resourceType",
          "role",
          "expires",
          "status"
        ]
      },
      "Chat": {
        "type": "object",
        "description": "A chat thread. Timestamps are ISO 8601 strings.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "chatType": {
            "type": "string",
            "enum": [
              "topic",
              "member",
              "submission",
              "ai",
              "support"
            ],
            "default": "topic",
            "description": "Topic chats hang off a project/asset/public/task, member chats are direct chats between members, submission chats belong to a review submission, ai and support chats are conversations with the assistant / support."
          },
          "topicId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the topic the chat is attached to."
          },
          "topicType": {
            "type": "string",
            "enum": [
              "asset",
              "project",
              "public",
              "task"
            ],
            "description": "The type of resource the chat is about."
          },
          "replyType": {
            "type": "string",
            "default": "quote",
            "description": "How replies are rendered in this chat."
          },
          "subject": {
            "type": "string",
            "nullable": true,
            "description": "The subject of the chat, such as an asset's file name."
          },
          "visibility": {
            "type": "string",
            "enum": [
              "creator",
              "reviewer",
              "member",
              "public"
            ],
            "description": "Controls which type of members can access the chat."
          },
          "publicId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Links to the Public release when visibility is `public` and topicType is `asset`."
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Users who have participated in the chat."
          },
          "following": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Users following the chat."
          },
          "annotations": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Annotations attached to the chat."
          },
          "totalMessages": {
            "type": "integer",
            "description": "Total number of messages in chat."
          },
          "lastMessageAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "recentMessages": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              ]
            },
            "description": "Array of IDs of messages of the most recent chats or the actual chats depending on request options."
          },
          "activeConvoId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The active convo (call) on this chat, if any."
          },
          "activeConvo": {
            "nullable": true,
            "description": "Alias of activeConvoId, or the populated convo when the request populates it.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "type": "object"
              }
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "pendingDelete"
            ],
            "description": "The status of the chat."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "chatType",
          "topicId",
          "topicType",
          "visibility"
        ]
      },
      "ChatMember": {
        "type": "object",
        "properties": {
          "scopeId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the scope the chat is about."
          },
          "scopeType": {
            "type": "string",
            "enum": [
              "project",
              "workspace",
              "social"
            ],
            "description": "Scope of chat. Determines who can be invited to the chat."
          },
          "scope": {
            "type": "object",
            "description": "Populated scope data (project or workspace) based on scopeType. Includes logo asset when populated.",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "description": "The ID of the scope (project or workspace)."
              },
              "name": {
                "type": "string",
                "description": "The name of the scope."
              },
              "slug": {
                "type": "string",
                "description": "The slug of the scope."
              },
              "logo": {
                "oneOf": [
                  {
                    "type": "string",
                    "format": "uuid"
                  },
                  {
                    "$ref": "#/components/schemas/Asset"
                  }
                ],
                "description": "The logo asset of the scope."
              }
            }
          },
          "subject": {
            "type": "string",
            "description": "The subject of the chat, such as an asset's file name."
          },
          "icon": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the asset used as the chat icon.",
            "nullable": true
          },
          "members": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only for private/memeber chats. Users who can currently access the chat. For project/asset chats access is determined by visibility and the topic."
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Users who have participated in the chat."
          },
          "totalMessages": {
            "type": "number",
            "description": "Total number of messages in chat."
          },
          "recentMessages": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              ],
              "description": "Array of IDs of messages of the most recent chats or the actual chats depending on request options."
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "pendingDelete"
            ],
            "description": "The status of the chat."
          },
          "createdBefore": {
            "type": "integer",
            "description": "Timestamp of query. Used to generate future paginated requests."
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the chat member object was created.",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the chat member object was last updated.",
            "example": 1678886400000
          }
        },
        "required": [
          "scopeId",
          "scopeType"
        ]
      },
      "ChatSubmission": {
        "type": "object",
        "properties": {
          "chatType": {
            "type": "string",
            "enum": [
              "submission"
            ],
            "default": "submission"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the project the chat is about"
          },
          "project": {
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/Project"
                }
              ]
            },
            "description": "Project ID or populated project data."
          },
          "publisherId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the user who published the submission."
          },
          "publisher": {
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/PublicUser"
                }
              ]
            },
            "description": "User ID or populated public user data."
          },
          "subject": {
            "type": "string",
            "description": "The subject of the chat, such as an asset's file name."
          },
          "description": {
            "type": "string",
            "description": "Description of the submission."
          },
          "version": {
            "type": "string",
            "description": "Version of the submission."
          },
          "assetIds": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              ]
            },
            "description": "Array of asset IDs or populatd assets associated with the submission."
          },
          "assetInventory": {
            "type": "object",
            "description": "Inventory of asset media type and their count. Example {image:6,video:7}"
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Users who have participated in the chat."
          },
          "annotations": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Array of annotations."
          },
          "totalMessages": {
            "type": "number",
            "description": "Total number of messages in chat."
          },
          "lastMessageAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp of the last message."
          },
          "recentMessages": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              ],
              "description": "Array of IDs of messages of the most recent chats or the actual chats depending on request options."
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Tags associated with the submission",
            "default": []
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "pendingDelete"
            ],
            "description": "The status of the chat"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Timestamp of creation",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Timestamp of last update",
            "example": 1678886400000
          }
        },
        "required": [
          "chatType",
          "projectId",
          "publisherId",
          "status"
        ]
      },
      "ChatMessage": {
        "type": "object",
        "description": "A message in a chat. `createdAt` and `updatedAt` are Unix millisecond timestamps.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "user",
              "system"
            ],
            "description": "Whether the message comes from a user or the system."
          },
          "dataType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "notification",
              "chatMessage",
              "convo"
            ],
            "description": "Type of resource `data` points to for system messages."
          },
          "dataId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "ID of the resource of `dataType`."
          },
          "data": {
            "description": "Alias of dataId. For system messages this may be populated with the referenced object of `dataType`.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "type": "object"
              }
            ]
          },
          "chatId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the chat the message belongs to."
          },
          "chatType": {
            "type": "string",
            "enum": [
              "topic",
              "member",
              "submission",
              "ai",
              "support"
            ],
            "description": "Type of the chat the message belongs to."
          },
          "authorId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The ID of the user who authored the message. Null for anonymous guest messages posted on a public release (see guest fields below)."
          },
          "author": {
            "description": "Populated author (a public user, or a synthesized guest author with isGuest true) when the request populates authors.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              },
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true
                  },
                  "displayName": {
                    "type": "string"
                  },
                  "color": {
                    "type": "string",
                    "nullable": true
                  },
                  "isGuest": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  }
                }
              }
            ]
          },
          "guestId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Client-generated identity of an anonymous guest commenter (localStorage UUID). Set only when authorId is null."
          },
          "guestName": {
            "type": "string",
            "nullable": true,
            "description": "Display name of an anonymous guest commenter. Set only when authorId is null."
          },
          "guestColor": {
            "type": "string",
            "nullable": true,
            "description": "Approved color an anonymous guest commenter selected. Set only when authorId is null."
          },
          "replyToId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "If this message is a reply to another this is the ID of the message it is a reply to.",
            "default": null
          },
          "content": {
            "type": "string",
            "nullable": true,
            "description": "The content of the chat message.",
            "maxLength": 10000
          },
          "mentions": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/PublicUser"
                }
              ]
            },
            "description": "IDs of users mentioned in the message, or the populated public users depending on request options."
          },
          "assetMentions": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "IDs of assets mentioned in the message."
          },
          "folderMentions": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "IDs of folders mentioned in the message."
          },
          "submissionMentions": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "IDs of submissions mentioned in the message."
          },
          "publicMentions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tokens of public releases mentioned in the message (short URL-safe strings, not UUIDs)."
          },
          "taskMentions": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "IDs of board tasks mentioned as chips in the message."
          },
          "taskCards": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "IDs of tasks rendered as full inline cards in the message."
          },
          "suggestedTasks": {
            "type": "array",
            "description": "Tasks suggested by the AI assistant for this message.",
            "items": {
              "type": "object",
              "properties": {
                "subject": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "assignedToId": {
                  "type": "string",
                  "format": "uuid"
                },
                "boardId": {
                  "type": "string",
                  "format": "uuid"
                },
                "columnId": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            }
          },
          "quotes": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "IDs of messages quoted by this message."
          },
          "attachments": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/Asset"
                }
              ]
            },
            "description": "Array of IDs of Assets attached to the chat or the actual Asset objects."
          },
          "revisions": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "When a message is edited a copy of the previous content is stored here as a revision."
          },
          "meta": {
            "type": "object",
            "nullable": true,
            "description": "Additional metadata for the chat message."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pendingDelete",
              "userDeleted"
            ],
            "description": "The status of the chat message."
          },
          "totalReplies": {
            "type": "integer",
            "description": "Total number of replies attached to message."
          },
          "recentReplies": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string",
                  "format": "uuid"
                },
                {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              ]
            },
            "description": "Array of IDs of Chat-Messages or the actual chats Chat-Message depending on request options."
          },
          "annotations": {
            "type": "array",
            "description": "Annotations attached to the message (dots, frame comments, drawings, ...). See the Annotation schemas.",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/DotValidation"
                },
                {
                  "$ref": "#/components/schemas/FrameCommentValidation"
                },
                {
                  "type": "object"
                }
              ]
            }
          },
          "linkPreviews": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkPreview"
            },
            "description": "Array of link previews extracted from the message content."
          },
          "reactions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reaction"
            },
            "description": "Array of reactions on this message from different users."
          },
          "highlighted": {
            "type": "boolean",
            "default": false
          },
          "highlightedById": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The user who highlighted the message."
          },
          "isConvoMessage": {
            "type": "boolean",
            "default": false,
            "description": "True for system messages generated by a convo (call) lifecycle event."
          },
          "scopeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The project (or other scope) the message belongs to."
          },
          "scopeType": {
            "type": "string",
            "nullable": true,
            "default": "project"
          },
          "scopeVisibility": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            }
          },
          "inheritanceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "aiRole": {
            "type": "string",
            "nullable": true,
            "enum": [
              "assistant",
              "tool"
            ],
            "description": "Set on messages authored by the AI assistant. Null on plain user messages."
          },
          "aiModel": {
            "type": "string",
            "nullable": true,
            "description": "Model identifier used to generate an assistant message."
          },
          "aiTokensIn": {
            "type": "integer",
            "nullable": true
          },
          "aiTokensOut": {
            "type": "integer",
            "nullable": true
          },
          "aiToolCalls": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object"
            }
          },
          "aiToolResults": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object"
            }
          },
          "aiUsageEventId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Timestamp of creation",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Timestamp of last update",
            "example": 1678886400000
          }
        },
        "required": [
          "id",
          "type",
          "chatId",
          "chatType"
        ]
      },
      "Reaction": {
        "type": "object",
        "properties": {
          "user": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the user who created the reaction."
          },
          "emoji": {
            "type": "string",
            "description": "The emoji character(s) used for the reaction.",
            "minLength": 1,
            "maxLength": 10,
            "example": "👍"
          }
        },
        "required": [
          "user",
          "emoji"
        ]
      },
      "LinkPreview": {
        "type": "object",
        "description": "Rich link preview metadata extracted from a URL (Open Graph / meta tags).",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The original URL."
          },
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Page title from og:title or <title> tag."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Page description from og:description or meta description."
          },
          "image": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Image URL from og:image."
          },
          "siteName": {
            "type": "string",
            "nullable": true,
            "description": "Site name from og:site_name."
          },
          "favicon": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Favicon URL."
          },
          "signature": {
            "type": "string",
            "description": "HMAC-SHA256 signature for anti-spoofing verification (transient, not persisted).",
            "pattern": "^[a-f0-9]{64}$"
          }
        },
        "required": [
          "url"
        ]
      },
      "Folder": {
        "type": "object",
        "description": "A folder in a project, submission or public file system. Timestamps are ISO 8601 strings.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the folder."
          },
          "ownerResourceId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the resource the folder belongs to."
          },
          "ownerResourceType": {
            "type": "string",
            "enum": [
              "project",
              "submission",
              "public"
            ],
            "description": "The type of resource the folder belongs to."
          },
          "creatorId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the user who created the folder."
          },
          "creator": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ],
            "description": "Populated creator, when the request populates it."
          },
          "publisherId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The user who published the folder, when published."
          },
          "inheritanceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "iconId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The ID of the asset used as the folder icon."
          },
          "icon": {
            "description": "Alias of iconId, or the populated icon asset when the request populates it. Omitted when the folder has no icon.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/Asset"
              }
            ]
          },
          "color": {
            "type": "string",
            "description": "Hex color code for the folder.",
            "nullable": true
          },
          "name": {
            "type": "string",
            "description": "The name of the folder."
          },
          "slug": {
            "type": "string",
            "nullable": true,
            "description": "URL-friendly version of the folder name."
          },
          "fileSystemPaths": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "File system paths the folder appears at."
          },
          "basePath": {
            "type": "string",
            "nullable": true,
            "description": "Base path of the folder within its file system."
          },
          "visibility": {
            "type": "string",
            "nullable": true,
            "enum": [
              "creator",
              "reviewer",
              "member",
              "public"
            ]
          },
          "noFileSystem": {
            "type": "boolean",
            "default": false,
            "description": "True when the folder is not represented in a file system tree."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Tags associated with the folder.",
            "default": []
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pendingDelete"
            ],
            "description": "The status of the folder.",
            "default": "active"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "ownerResourceId",
          "ownerResourceType",
          "creatorId",
          "name"
        ]
      },
      "File": {
        "type": "object",
        "properties": {
          "assetId": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the asset the file belongs to."
          },
          "keyPath": {
            "type": "string",
            "description": "The path the file is available at when added to the base media url.",
            "example": "/project/asset/file123.jpg"
          },
          "checksum": {
            "type": "string",
            "description": "The checksum of the file."
          },
          "checksumAlgorithm": {
            "type": "string",
            "enum": [
              "md5"
            ],
            "description": "This is a placeholder for sometime in the future where we may require multiple hashing algorithms.",
            "default": "md5"
          },
          "name": {
            "type": "string",
            "description": "The full name of the file, including extension."
          },
          "functionType": {
            "type": "string",
            "enum": [
              "original",
              "thumbnail",
              "stream",
              "media",
              "customThumbnailOriginal",
              "customThumbnail",
              "documentText"
            ],
            "description": "This type is used to determine the file's purpose, not the file type (video, image, etc.)."
          },
          "sizeInBytes": {
            "type": "number",
            "description": "The size of the file in bytes."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pendingDelete",
              "inactive"
            ],
            "description": "The status of the file.",
            "default": "active"
          },
          "meta": {
            "type": "object",
            "description": "Additional metadata for the file.",
            "default": {}
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the file was created.",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the file was last updated.",
            "example": 1678886400000
          }
        },
        "required": [
          "assetId",
          "keyPath",
          "name",
          "sizeInBytes",
          "status"
        ]
      },
      "Asset": {
        "type": "object",
        "description": "An uploaded asset (image, video, audio, document, 3D model or generic file). `createdAt`, `updatedAt` and `publishedOn` are Unix millisecond timestamps.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the asset"
          },
          "ownerResourceId": {
            "type": "string",
            "format": "uuid",
            "description": "The id of the resource that the asset is attached to."
          },
          "ownerResourceType": {
            "type": "string",
            "enum": [
              "workspace",
              "project",
              "user",
              "chat",
              "chatMessage"
            ],
            "description": "The type of the owner resource."
          },
          "creatorId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the user who created the asset."
          },
          "creator": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ],
            "description": "Populated creator, when the request populates it."
          },
          "fileSystemPaths": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "File system paths the asset appears at."
          },
          "basePath": {
            "type": "string",
            "nullable": true,
            "description": "Base path of the asset within its file system."
          },
          "noFileSystem": {
            "type": "boolean",
            "default": false,
            "description": "True when the asset is not represented in a file system tree (avatars, logos, chat attachments)."
          },
          "keyPath": {
            "type": "string",
            "description": "The storage key prefix used for the asset's files. Needed by clients to construct CDN URLs."
          },
          "checksum": {
            "type": "string",
            "description": "Checksum of the original file."
          },
          "checksumAlgorithm": {
            "type": "string",
            "nullable": true,
            "example": "md5",
            "description": "The algorithm used for computing the checksum."
          },
          "name": {
            "type": "string",
            "description": "The name of the asset used on the platform."
          },
          "slug": {
            "type": "string",
            "nullable": true,
            "description": "URL-safe slug generated from the asset name with UUID."
          },
          "functionType": {
            "type": "string",
            "enum": [
              "attachment",
              "media",
              "avatar",
              "logo",
              "icon"
            ],
            "description": "The function of the asset on the platform."
          },
          "mediaType": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "document",
              "3d",
              "file"
            ],
            "description": "The media type of the asset."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Tags associated with the asset.",
            "default": []
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/File"
            },
            "description": "The files associated with the asset. Internal fields (bucketName, internalUrl, postProcessingTasks) are stripped."
          },
          "chats": {
            "type": "object",
            "description": "Chat information for the asset",
            "properties": {
              "creatorLastMessageAt": {
                "type": "string",
                "format": "date-time",
                "description": "Timestamp of last creator chat message"
              },
              "reviewerLastMessageAt": {
                "type": "string",
                "format": "date-time",
                "description": "Timestamp of last reviewer chat message"
              },
              "creator": {
                "type": "string",
                "format": "uuid",
                "description": "Creator chat ID"
              },
              "reviewer": {
                "type": "string",
                "format": "uuid",
                "description": "Reviewer chat ID"
              }
            }
          },
          "expectedUploadSizeInMB": {
            "type": "number",
            "description": "Expected upload size in megabytes.",
            "default": 0
          },
          "sizeInBytes": {
            "type": "number",
            "description": "The total size of the asset in bytes.",
            "default": 0
          },
          "visibility": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer",
                "member",
                "public"
              ]
            },
            "description": "The visibility settings for the asset.",
            "default": [
              "creator"
            ]
          },
          "publishedOn": {
            "type": "integer",
            "format": "int64",
            "nullable": true,
            "description": "Unix millisecond timestamp when the asset was published. Null if not published.",
            "example": 1678886400000
          },
          "publisherId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "ID of the user who published the asset"
          },
          "publisher": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ],
            "description": "Populated publisher, when the request populates it."
          },
          "hasActivePublicLink": {
            "type": "boolean",
            "default": false,
            "description": "True while at least one public asset link for the asset is active."
          },
          "hasActivePublicFileSystem": {
            "type": "boolean",
            "default": false,
            "description": "True while the asset is part of at least one active public release."
          },
          "everPublic": {
            "type": "boolean",
            "default": false,
            "description": "True once the asset has ever been shared publicly."
          },
          "publicFileSystemIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Public releases the asset belongs to."
          },
          "publicAssetLinkIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Public asset links created for the asset."
          },
          "submissionFileSystemIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Submissions the asset belongs to."
          },
          "aiGenerated": {
            "type": "boolean",
            "default": false,
            "description": "True when the asset's bytes were produced by a platform AI process (e.g. an AI revision)."
          },
          "promotedFromAssetId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "When the asset was created by promoting a chat attachment into a project, the source attachment asset's ID."
          },
          "contentSnippet": {
            "type": "string",
            "nullable": true,
            "description": "Leading excerpt of a document's extracted text."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pendingUpload",
              "pendingPostProcessing",
              "postProcessingError",
              "pendingDelete",
              "inactive"
            ],
            "description": "The status of the asset.",
            "default": "active"
          },
          "meta": {
            "type": "object",
            "nullable": true,
            "description": "Additional metadata for the asset (dimensions, duration, document info, ...)."
          },
          "inheritanceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "ID of the inheritance record for this asset"
          },
          "inheritance": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Inheritance"
              }
            ],
            "description": "Populated inheritance record, when the request populates it."
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix millisecond timestamp when the asset was created.",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Unix millisecond timestamp when the asset was last updated.",
            "example": 1678886400000
          }
        },
        "required": [
          "id",
          "ownerResourceId",
          "ownerResourceType",
          "creatorId",
          "name",
          "functionType",
          "mediaType",
          "status"
        ]
      },
      "Inheritance": {
        "type": "object",
        "description": "Permission inheritance chain for a resource. `inheritance` maps each ancestor resource ID (and the resource itself) to its resource type; `inheritanceInverted` maps resource type to resource ID.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceType": {
            "type": "string"
          },
          "inheritance": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "example": {
              "0192a3b4-5c6d-7e8f-9a0b-1c2d3e4f5a6b": "workspace",
              "0192a3b4-5c6d-7e8f-9a0b-1c2d3e4f5a6c": "project"
            }
          },
          "inheritanceInverted": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "format": "uuid"
            }
          },
          "path": {
            "type": "string",
            "description": "Slash separated path of ancestor IDs."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Projects": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Project"
        }
      },
      "Project": {
        "type": "object",
        "description": "A project within a workspace. `createdAt` and `updatedAt` are Unix millisecond timestamps.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the project"
          },
          "workspaceId": {
            "type": "string",
            "format": "uuid",
            "description": "Id of workspace project belongs to."
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "The name of the project"
          },
          "slug": {
            "type": "string",
            "description": "URL-friendly version of the project name"
          },
          "color": {
            "type": "string",
            "description": "Hex color code for the project. Omitted when not set."
          },
          "logo": {
            "description": "The project logo asset ID, or the populated logo Asset when the request populates it. Omitted when the project has no logo.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/Asset"
              }
            ]
          },
          "settings": {
            "type": "object",
            "description": "Per-project setting overrides (currently AI feature flags). Only present when the project overrides at least one key; absent keys inherit from the workspace settings.",
            "additionalProperties": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "pendingDelete"
            ],
            "description": "The status of the project",
            "default": "active"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the project was created.",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the project was last updated.",
            "example": 1678886400000
          }
        },
        "required": [
          "id",
          "workspaceId",
          "name",
          "status"
        ]
      },
      "Workspace": {
        "type": "object",
        "description": "A workspace. `createdAt` and `updatedAt` are Unix millisecond timestamps.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "maxLength": 350,
            "nullable": true
          },
          "logo": {
            "description": "The workspace logo asset ID, or the populated logo Asset when the request populates it. Omitted when the workspace has no logo.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/Asset"
              }
            ]
          },
          "icon": {
            "description": "The workspace icon asset ID, or the populated icon Asset when the request populates it. Omitted when the workspace has no icon.",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/Asset"
              }
            ]
          },
          "color": {
            "description": "Hex color code.",
            "type": "string",
            "nullable": true
          },
          "settings": {
            "type": "object",
            "description": "Workspace settings (AI feature flags such as aiChatEnabled, aiAssistEnabled, aiImageRevisionEnabled, dailyUserAiSpendBudget, ...). Only present when the workspace has changed a setting from the defaults.",
            "additionalProperties": true
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The requesting user's roles on the workspace. Only present on endpoints that add it (e.g. listing a user's workspaces)."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "pendingDelete"
            ]
          },
          "createdAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the workspace was created.",
            "example": 1678886400000
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64",
            "description": "The timestamp when the workspace was last updated.",
            "example": 1678886400000
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "status"
        ]
      },
      "User": {
        "type": "object",
        "description": "The authenticated user's own profile. Timestamps are ISO 8601 strings. Auth secrets (password, MFA secrets/backup codes) and internal fields (linkedAuthProviders, resourceLastSeen, resourceSettings) are never returned.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "User's Id"
          },
          "firstName": {
            "type": "string",
            "nullable": true,
            "description": "The first name of the user.",
            "maxLength": 35
          },
          "middleName": {
            "type": "string",
            "nullable": true,
            "description": "The middle name of the user.",
            "maxLength": 35
          },
          "lastName": {
            "type": "string",
            "nullable": true,
            "description": "The last name of the user.",
            "maxLength": 35
          },
          "company": {
            "type": "string",
            "nullable": true,
            "description": "The company of the user.",
            "maxLength": 100
          },
          "userName": {
            "type": "string",
            "nullable": true,
            "description": "The username of the user.",
            "pattern": "^[a-zA-Z0-9_.-]+$",
            "maxLength": 35
          },
          "displayName": {
            "type": "string",
            "nullable": true,
            "description": "The display name of the user.",
            "maxLength": 50
          },
          "avatarId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "ID of the user's avatar asset."
          },
          "avatar": {
            "description": "The populated avatar Asset (or its ID when not populated).",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/Asset"
              }
            ]
          },
          "color": {
            "type": "string",
            "description": "Hex color assigned to the user, used for avatars and cursors.",
            "default": "#ffffff"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The email address of the user."
          },
          "isEmailVerified": {
            "type": "boolean",
            "default": false,
            "description": "Indicates whether the user's email is verified."
          },
          "hasPassword": {
            "type": "boolean",
            "default": false,
            "description": "False for accounts created via an OAuth provider that have not set a password."
          },
          "allowOauthAutolink": {
            "type": "boolean",
            "default": true,
            "description": "Whether an OAuth sign-in with a matching verified email may be linked to this account automatically."
          },
          "mfaEnabled": {
            "type": "boolean",
            "default": false,
            "description": "Indicates whether multi-factor authentication (MFA) is enabled for the user."
          },
          "mfaType": {
            "type": "string",
            "enum": [
              "totp"
            ],
            "default": "totp",
            "description": "The type of multi-factor authentication (MFA) used by the user."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "locked",
              "pendingDelete"
            ],
            "default": "active",
            "description": "The status of the user."
          },
          "accountType": {
            "type": "string",
            "enum": [
              "standard",
              "guest"
            ],
            "default": "standard",
            "description": "Guest accounts are created from public release comment flows and can be upgraded to standard accounts."
          },
          "globalRoles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Platform-wide roles (e.g. staff)."
          },
          "preferences": {
            "type": "object",
            "description": "The user's preferences (hide, emailNotifications, systemNotifications, sounds, dateFormat, ...).",
            "additionalProperties": true
          },
          "language": {
            "type": "string",
            "default": "en"
          },
          "externalIds": {
            "type": "object",
            "description": "IDs of the user in external systems (e.g. the payment provider customer ID).",
            "additionalProperties": true
          },
          "lastSeen": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the user was last active."
          },
          "hasUsedTrial": {
            "type": "boolean",
            "default": false
          },
          "hasUsedDemo": {
            "type": "boolean",
            "default": false
          },
          "hasSeen": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "One-time UI elements the user has already seen (e.g. welcomeVideo)."
          },
          "awards": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "staff",
                "earlyAdopter"
              ]
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "email",
          "status",
          "accountType"
        ]
      },
      "PublicUser": {
        "type": "object",
        "description": "The subset of a user's profile visible to other users.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "User's Id"
          },
          "firstName": {
            "type": "string",
            "nullable": true,
            "description": "The first name of the user.",
            "maxLength": 35
          },
          "lastName": {
            "type": "string",
            "nullable": true,
            "description": "The last name of the user.",
            "maxLength": 35
          },
          "displayName": {
            "type": "string",
            "nullable": true,
            "description": "The display name of the user.",
            "maxLength": 50
          },
          "avatar": {
            "description": "The populated avatar Asset (or its ID when not populated).",
            "oneOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "$ref": "#/components/schemas/Asset"
              }
            ]
          },
          "color": {
            "type": "string",
            "description": "Hex color assigned to the user."
          },
          "awards": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "staff",
                "earlyAdopter"
              ]
            }
          },
          "accountType": {
            "type": "string",
            "enum": [
              "standard",
              "guest"
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "code": {
            "type": "number"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "CursorPaginatedResult": {
        "type": "object",
        "properties": {
          "nextCursor": {
            "type": "string",
            "nullable": true
          },
          "prevCursor": {
            "type": "string",
            "nullable": true
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "totalResults": {
            "type": "integer",
            "description": "Total number of results matching the query (may be approximate or unavailable depending on pagination settings).",
            "nullable": true
          },
          "totalPreviousResults": {
            "type": "integer",
            "description": "Estimated number of results before the current page.",
            "nullable": true
          },
          "totalNextResults": {
            "type": "integer",
            "description": "Estimated number of results after the current page.",
            "nullable": true
          },
          "queryAt": {
            "type": "integer",
            "description": "Timestamp of when the query was executed."
          }
        },
        "required": [
          "hasNextPage",
          "hasPrevPage"
        ]
      },
      "CursorPaginatedFeed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CursorPaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Asset"
                    },
                    {
                      "properties": {
                        "chat": {
                          "$ref": "#/components/schemas/Chat"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      },
      "PaginatedResult": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer",
            "description": "The current page number."
          },
          "limit": {
            "type": "integer",
            "description": "The maximum number of results per page."
          },
          "totalPages": {
            "type": "integer",
            "description": "The total number of pages."
          },
          "totalResults": {
            "type": "integer",
            "description": "The total number of results across all pages."
          },
          "queryAt": {
            "type": "integer",
            "description": "Timestamp of query."
          }
        },
        "required": [
          "page",
          "limit",
          "totalPages",
          "totalResults"
        ]
      },
      "Tags": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Tag"
        }
      },
      "Tag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The unique identifier of the tag"
          },
          "name": {
            "type": "string",
            "description": "The display name of the tag",
            "maxLength": 50
          },
          "slug": {
            "type": "string",
            "description": "URL-friendly version of the tag name"
          },
          "ownerResourceType": {
            "type": "string",
            "enum": [
              "project"
            ],
            "description": "The type of resource that owns this tag (currently limited to project only)"
          },
          "ownerResourceId": {
            "type": "string",
            "format": "uuid",
            "description": "The ID of the resource that owns this tag"
          },
          "color": {
            "type": "string",
            "description": "Hex color code for the tag from predefined color list",
            "enum": [
              "#37474F",
              "#FF5722",
              "#2962FF",
              "#33691E",
              "#00796B",
              "#455A64",
              "#2979FF",
              "#827717",
              "#7986CB",
              "#8E24AA",
              "#9575CD",
              "#BF360C",
              "#01579B",
              "#EF6C00",
              "#AA00FF",
              "#F44336",
              "#7C4DFF",
              "#E65100",
              "#8D6E63",
              "#283593",
              "#607D8B",
              "#009688",
              "#FF5252",
              "#03A9F4",
              "#C2185B",
              "#00ACC1",
              "#E91E63",
              "#5D4037",
              "#78909C",
              "#1E88E5",
              "#D500F9",
              "#7E57C2",
              "#5C6BC0",
              "#558B2F",
              "#2E7D32",
              "#F50057",
              "#004D40",
              "#0D47A1",
              "#C51162",
              "#D50000",
              "#6200EA",
              "#00BCD4",
              "#0277BD"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp when the tag was created",
            "example": "2023-03-15T12:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp when the tag was last updated",
            "example": "2023-03-15T12:00:00.000Z"
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "ownerResourceType",
          "ownerResourceId",
          "color"
        ]
      },
      "Config": {
        "type": "object",
        "properties": {
          "limits": {
            "type": "object",
            "properties": {
              "chat": {
                "type": "object",
                "properties": {
                  "maxSubjectCharacterLength": {
                    "type": "integer",
                    "description": "Maximum character length for chat subjects."
                  },
                  "maxContentCharacterLength": {
                    "type": "integer",
                    "description": "Maximum character length for chat message content."
                  },
                  "numberOfRecentMessages": {
                    "type": "integer",
                    "description": "Number of recent messages to retrieve for a chat."
                  },
                  "numberOfRecentReplies": {
                    "type": "integer",
                    "description": "Number of recent replies to retrieve for a message."
                  },
                  "maxMessageResults": {
                    "type": "integer",
                    "description": "Maximum number of messages to return in a single query."
                  },
                  "maxReplyResults": {
                    "type": "integer",
                    "description": "Maximum number of replies to return in a single query."
                  },
                  "maxMemberChats": {
                    "type": "integer",
                    "description": "Maximum number of member chats a user can participate in."
                  },
                  "maxMemberChatRecentMessages": {
                    "type": "integer",
                    "description": "Maximum number of recent messages to retrieve for member chats."
                  }
                }
              },
              "chatMessage": {
                "type": "object",
                "properties": {
                  "maxNumberOfAttachments": {
                    "type": "integer",
                    "description": "Maximum number of attachments allowed per chat message."
                  },
                  "maxNumberOfAnnotations": {
                    "type": "integer",
                    "description": "Maximum number of annotations allowed per chat message."
                  },
                  "maxNumberOfMentions": {
                    "type": "integer",
                    "description": "Maximum number of user mentions allowed per chat message."
                  }
                }
              },
              "asset": {
                "type": "object",
                "properties": {
                  "maxNameLength": {
                    "type": "integer",
                    "description": "Maximum character length for asset names."
                  },
                  "maxDownloadResults": {
                    "type": "integer",
                    "description": "Maximum number of assets that can be downloaded in a single request."
                  },
                  "maxRepairResults": {
                    "type": "integer",
                    "description": "Maximum number of assets that can be repaired in a single request."
                  },
                  "maxPublishResults": {
                    "type": "integer",
                    "description": "Maximum number of assets that can be published in a single request."
                  }
                }
              },
              "membership": {
                "type": "object",
                "properties": {
                  "maxMembershipRecords": {
                    "type": "integer",
                    "description": "Maximum number of membership records allowed."
                  }
                }
              },
              "project": {
                "type": "object",
                "properties": {
                  "maxProjects": {
                    "type": "integer",
                    "description": "Maximum number of projects a user can create or be a member of."
                  },
                  "maxNameLength": {
                    "type": "integer",
                    "description": "Maximum character length for project names."
                  },
                  "maxDescriptionLength": {
                    "type": "integer",
                    "description": "Maximum character length for project descriptions."
                  },
                  "maxFeedResults": {
                    "type": "integer",
                    "description": "Maximum number of results to return in the project feed."
                  }
                }
              },
              "workspace": {
                "type": "object",
                "properties": {
                  "maxWorkspacesPerUser": {
                    "type": "integer",
                    "description": "Maximum number of workspaces a user can be associated with."
                  },
                  "maxNameLength": {
                    "type": "integer",
                    "description": "Maximum character length for workspace names."
                  },
                  "maxDescriptionLength": {
                    "type": "integer",
                    "description": "Maximum character length for workspace descriptions."
                  }
                }
              },
              "folder": {
                "type": "object",
                "properties": {
                  "maxNameLength": {
                    "type": "integer",
                    "description": "Maximum character length for folder names."
                  },
                  "maxDescriptionLength": {
                    "type": "integer",
                    "description": "Maximum character length for folder descriptions."
                  },
                  "maxFolders": {
                    "type": "integer",
                    "description": "The maximum number of folders a user can have."
                  }
                }
              },
              "user": {
                "type": "object",
                "properties": {
                  "minPasswordLength": {
                    "type": "integer",
                    "description": "Minimum required length for user passwords."
                  },
                  "minUserNameLength": {
                    "type": "integer",
                    "description": "Minimum required length for usernames."
                  },
                  "maxUserNameLength": {
                    "type": "integer",
                    "description": "Maximum allowed length for usernames."
                  },
                  "maxFirstNameLength": {
                    "type": "integer",
                    "description": "Maximum allowed length for user first names."
                  },
                  "maxMiddleNameLength": {
                    "type": "integer",
                    "description": "Maximum allowed length for user middle names."
                  },
                  "maxLastNameLength": {
                    "type": "integer",
                    "description": "Maximum allowed length for user last names."
                  },
                  "maxCompanyNameLength": {
                    "type": "integer",
                    "description": "Maximum allowed length for company names."
                  },
                  "maxDisplayNameLength": {
                    "type": "integer",
                    "description": "Maximum allowed length for display names."
                  }
                }
              },
              "notification": {
                "type": "object",
                "properties": {
                  "maxNotificationResults": {
                    "type": "integer",
                    "description": "Maximum number of notifications to return in a single query."
                  },
                  "maxNumberOfChannels": {
                    "type": "integer",
                    "description": "Maximum number of channels to retrieve notification results for."
                  }
                }
              },
              "task": {
                "type": "object",
                "properties": {
                  "maxTaskResults": {
                    "type": "integer",
                    "description": "Maximum number of tasks to return in a single query."
                  }
                }
              },
              "subscription": {
                "type": "object",
                "properties": {
                  "maxStarterProducts": {
                    "type": "integer",
                    "description": "Maximum number of starter products allowed per subscription."
                  }
                }
              },
              "summary": {
                "type": "object",
                "properties": {
                  "maxSummaryResults": {
                    "type": "integer",
                    "description": "Maximum number of summaries to return in a single query."
                  }
                }
              },
              "maxAssets": {
                "type": "integer",
                "description": "The maximum number of assets a user can have."
              },
              "tag": {
                "type": "object",
                "properties": {
                  "maxNameLength": {
                    "type": "integer",
                    "description": "Maximum character length for tag names."
                  },
                  "maxTagsPerResource": {
                    "type": "integer",
                    "description": "Maximum number of tags allowed per resource."
                  }
                }
              }
            }
          },
          "colors": {
            "type": "object",
            "properties": {
              "approvedColors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ResourceSettings": {
        "type": "object",
        "properties": {
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "workspace",
              "project"
            ]
          },
          "emailNotifications": {
            "type": "object",
            "additionalProperties": true
          },
          "systemNotifications": {
            "type": "object",
            "additionalProperties": true
          },
          "sounds": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "resourceId",
          "resourceType"
        ]
      },
      "EffectiveSettings": {
        "type": "object",
        "properties": {
          "emailNotifications": {
            "type": "object",
            "additionalProperties": true
          },
          "systemNotifications": {
            "type": "object",
            "additionalProperties": true
          },
          "sounds": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "AiTone": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "professional",
              "casual",
              "concise",
              "friendly",
              "formal",
              "cleanup"
            ]
          },
          "label": {
            "type": "string"
          }
        }
      },
      "AiBalanceResponse": {
        "type": "object",
        "properties": {
          "balance": {
            "type": "integer",
            "description": "Spendable Nurama Credit balance for the workspace, in credits (`planBalance + purchasedBalance`)."
          },
          "planBalance": {
            "type": "integer",
            "description": "Credits from the monthly plan grant. Refreshes each period; does not carry over."
          },
          "purchasedBalance": {
            "type": "integer",
            "description": "Credits from top-up purchases. Accumulates."
          },
          "hasPriorTopUp": {
            "type": "boolean",
            "description": "Whether the workspace has ever made a manual credit purchase. Precondition for enabling auto top-up."
          }
        }
      },
      "AiComposeResponse": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "Nu's commentary for this turn."
          },
          "proposal": {
            "type": "string",
            "nullable": true,
            "description": "The message Nu proposes posting, or null when it has none yet."
          },
          "billedCredits": {
            "type": "integer"
          },
          "balanceAfter": {
            "type": "integer",
            "nullable": true
          },
          "eventId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "AiUsageReportResponse": {
        "type": "object",
        "properties": {
          "total": {
            "type": "object",
            "properties": {
              "billedCredits": {
                "type": "integer"
              },
              "callCount": {
                "type": "integer"
              },
              "tokensIn": {
                "type": "integer"
              },
              "tokensOut": {
                "type": "integer"
              }
            }
          },
          "byUser": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "userId": {
                  "type": "string",
                  "format": "uuid"
                },
                "displayName": {
                  "type": "string",
                  "nullable": true
                },
                "avatar": {
                  "nullable": true,
                  "description": "The user's avatar asset, when they have one."
                },
                "billedCredits": {
                  "type": "integer"
                },
                "callCount": {
                  "type": "integer"
                }
              }
            }
          },
          "byIntegration": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "integrationPoint": {
                  "type": "string",
                  "enum": [
                    "polish",
                    "chat",
                    "taskGeneration",
                    "imageRevision",
                    "convo"
                  ]
                },
                "billedCredits": {
                  "type": "integer"
                },
                "callCount": {
                  "type": "integer"
                }
              }
            }
          },
          "filters": {
            "type": "object",
            "description": "The effective filters after defaults were applied.",
            "properties": {
              "startDate": {
                "type": "string",
                "format": "date-time"
              },
              "endDate": {
                "type": "string",
                "format": "date-time"
              },
              "userId": {
                "type": "string",
                "format": "uuid",
                "nullable": true
              },
              "integrationPoint": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "AiPolishResponse": {
        "type": "object",
        "properties": {
          "polishedText": {
            "type": "string",
            "description": "The rewritten draft. Trimmed; never empty on a 200."
          },
          "billedCredits": {
            "type": "integer",
            "description": "Credits actually deducted for this call: the provider cost of the\ntokens used, multiplied by Nurama's markup and rounded up to a\nwhole credit.\n"
          },
          "balanceAfter": {
            "type": "integer",
            "nullable": true,
            "description": "Workspace credit balance immediately after deduction."
          },
          "eventId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "ID of the usage event recorded for this call. May be `null` in\nthe rare case where the deduction succeeded but the event could\nnot be recorded.\n"
          }
        }
      },
      "AiTaskGenerationResponse": {
        "type": "object",
        "properties": {
          "tasks": {
            "type": "array",
            "description": "Generated task drafts. Always at least one entry on a 200 (a\nresult with zero usable tasks is reported as a 502\n`aiTaskGenerationInvalidOutput`). At most 10 entries.\n",
            "items": {
              "type": "object",
              "properties": {
                "subject": {
                  "type": "string",
                  "description": "Short imperative title (≤500 chars after trimming)."
                },
                "description": {
                  "type": "string",
                  "description": "Optional expansion (≤20000 chars after trimming); may be empty."
                }
              }
            }
          },
          "billedCredits": {
            "type": "integer",
            "description": "Credits actually deducted for this call. Same accounting model\nas `AiPolishResponse.billedCredits`.\n"
          },
          "balanceAfter": {
            "type": "integer",
            "nullable": true,
            "description": "Workspace credit balance immediately after deduction."
          },
          "eventId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "ID of the usage event recorded for this call."
          }
        }
      },
      "AiChatTopic": {
        "type": "object",
        "description": "One AI chat topic. Private to its `creatorId`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "chatType": {
            "type": "string",
            "enum": [
              "ai"
            ]
          },
          "scopeType": {
            "type": "string",
            "enum": [
              "workspace",
              "project",
              "social"
            ]
          },
          "scopeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Null when `scopeType` is `social`."
          },
          "billedResourceType": {
            "type": "string",
            "enum": [
              "workspace",
              "user"
            ],
            "description": "`workspace` for AI chat topics; `user` for support topics."
          },
          "billedResourceId": {
            "type": "string",
            "format": "uuid",
            "description": "The billing workspace id. Always set, even for social topics."
          },
          "creatorId": {
            "type": "string",
            "format": "uuid"
          },
          "chatId": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the chat that holds the topic's messages. Use it with `GET /v1/chats/{chatId}/messages` and `POST /v1/chats/{chatId}/new-message`."
          },
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Null until the assistant auto-names the topic or the user sets one."
          },
          "titleGenerationFailed": {
            "type": "boolean"
          },
          "totalMessages": {
            "type": "integer"
          },
          "totalBilledCredits": {
            "type": "integer"
          },
          "lastMessagePreview": {
            "type": "string",
            "nullable": true,
            "description": "Truncated plain-text snapshot of the most recent message."
          },
          "lastMessageAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "contextItems": {
            "type": "array",
            "nullable": true,
            "description": "Context-column pins (`{ type, id }`), see `PATCH /ai/chat/topics/{topicId}`.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "asset",
                    "folder",
                    "user",
                    "submission",
                    "public"
                  ]
                },
                "id": {
                  "type": "string"
                }
              }
            }
          },
          "archived": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pendingDelete"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AiChatTopicListResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiChatTopic"
            }
          }
        }
      },
      "AiChatCreateTopicResponse": {
        "type": "object",
        "description": "Newly-created (always-empty) topic. Send the first message via\nPOST /v1/chats/{topic.chatId}/new-message.\n",
        "properties": {
          "topic": {
            "$ref": "#/components/schemas/AiChatTopic"
          }
        }
      },
      "Board": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "creatorId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "visibility": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived",
              "pendingDelete"
            ]
          },
          "pendingDeleteAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Set when `status` is `pendingDelete`; the time after which the cleanup pass may hard-delete the board."
          },
          "followers": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "User IDs following this board"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Tag IDs applied to this board (project-scoped tags)"
          },
          "sortOrder": {
            "type": "number"
          },
          "inheritanceId": {
            "type": "string",
            "format": "uuid"
          },
          "columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BoardColumn"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BoardWithTasks": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Board"
          },
          {
            "type": "object",
            "properties": {
              "columns": {
                "type": "array",
                "items": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/BoardColumn"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "tasks": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Task"
                          }
                        },
                        "taskCount": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      },
      "BoardColumn": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "boardId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true,
            "description": "Hex color code"
          },
          "isDefault": {
            "type": "boolean"
          },
          "taskStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pending",
              "inProgress",
              "complete",
              "closed"
            ],
            "description": "Status applied to tasks moved into this column"
          },
          "reviewersCanContribute": {
            "type": "boolean"
          },
          "sortOrder": {
            "type": "number"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TaskLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "linkedTaskId": {
            "type": "string",
            "format": "uuid"
          },
          "linkType": {
            "type": "string",
            "enum": [
              "related",
              "blocks",
              "blockedBy",
              "duplicate"
            ]
          },
          "linkedTask": {
            "$ref": "#/components/schemas/Task"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BotUser": {
        "type": "object",
        "description": "Bot user profile. Authentication fields are never included.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "color": {
            "type": "string"
          },
          "accountType": {
            "type": "string",
            "enum": [
              "bot"
            ]
          },
          "status": {
            "type": "string"
          },
          "avatarId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "avatar": {
            "nullable": true,
            "description": "The bot's avatar asset, or null.",
            "allOf": [
              {
                "$ref": "#/components/schemas/Asset"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BotApiKeySummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "keyPrefix": {
            "type": "string",
            "description": "8-char plaintext lookup prefix (the part after `nrm_bot_` and before the secret)"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "nullable": true,
            "example": "botAccess"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BotProjectMembership": {
        "type": "object",
        "properties": {
          "membershipId": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "projectName": {
            "type": "string"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer",
                "projectAdmin"
              ]
            }
          }
        }
      },
      "MentionableSubmission": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/UUID"
          },
          "subject": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "version": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "totalMessages": {
            "type": "integer"
          },
          "lastMessageAt": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "publishedAt": {
            "type": "integer",
            "format": "int64"
          },
          "chatId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Same as `id` - the submission row is also its chat."
          },
          "creator": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ]
          }
        }
      },
      "MentionablePublic": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/UUID"
          },
          "token": {
            "type": "string",
            "description": "Share token used in `{{publicMention:token}}`."
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "expiresAt": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "createdAt": {
            "type": "integer",
            "format": "int64"
          },
          "chatId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "nullable": true,
            "description": "ID of the release's public topic chat, if one exists."
          },
          "creator": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ]
          }
        }
      },
      "WorkspaceProjectChat": {
        "type": "object",
        "properties": {
          "project": {
            "type": "object",
            "properties": {
              "id": {
                "$ref": "#/components/schemas/UUID"
              },
              "name": {
                "type": "string"
              },
              "logo": {
                "nullable": true,
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Asset"
                  }
                ]
              },
              "color": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "visibility": {
            "type": "string",
            "enum": [
              "creator",
              "reviewer"
            ]
          },
          "chat": {
            "type": "object",
            "properties": {
              "id": {
                "$ref": "#/components/schemas/UUID"
              },
              "topicType": {
                "type": "string",
                "enum": [
                  "project"
                ]
              },
              "topicId": {
                "$ref": "#/components/schemas/UUID"
              },
              "visibility": {
                "type": "string",
                "enum": [
                  "creator",
                  "reviewer"
                ]
              },
              "participants": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/UUID"
                }
              },
              "totalMessages": {
                "type": "integer"
              },
              "recentMessages": {
                "type": "array",
                "maxItems": 1,
                "description": "The latest message in the chat, if any.",
                "items": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              },
              "createdAt": {
                "type": "integer",
                "format": "int64"
              },
              "updatedAt": {
                "type": "integer",
                "format": "int64"
              }
            }
          }
        }
      },
      "ProjectScopeResponse": {
        "type": "object",
        "properties": {
          "projectMembership": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserRoleResource"
            }
          },
          "workspaceMembership": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserRoleResource"
            }
          }
        }
      },
      "WorkspaceScopeResponse": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/UserRoleResource"
        }
      },
      "UserRoleResource": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "example": "6661714bb26e266ece994286"
          },
          "resourceId": {
            "type": "string",
            "example": "6661714bb26e266ece99427f"
          },
          "resourceType": {
            "type": "string",
            "example": "project"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "projectOwner"
            ]
          },
          "createdAt": {
            "type": "integer",
            "example": 1717662028244
          },
          "updatedAt": {
            "type": "integer",
            "example": 1717662028244
          },
          "id": {
            "type": "string",
            "example": "6661714cb26e266ece99428b"
          },
          "resource": {
            "type": "object",
            "properties": {
              "workspaceId": {
                "type": "string",
                "example": "6661714bb26e266ece994278"
              },
              "name": {
                "type": "string",
                "example": "McGlynn Group"
              },
              "slug": {
                "type": "string",
                "example": "mcglynn-group"
              },
              "status": {
                "type": "string",
                "example": "active"
              },
              "createdAt": {
                "type": "integer",
                "example": 1717662027953
              },
              "updatedAt": {
                "type": "integer",
                "example": 1717662027953
              },
              "id": {
                "type": "string",
                "example": "6661714bb26e266ece99427f"
              }
            }
          },
          "user": {
            "type": "object",
            "properties": {
              "firstName": {
                "type": "string",
                "example": "Rory"
              },
              "lastName": {
                "type": "string",
                "example": "Mann"
              },
              "displayName": {
                "type": "string",
                "example": "Catherine"
              },
              "id": {
                "type": "string",
                "example": "6661714bb26e266ece994286"
              }
            }
          }
        },
        "required": [
          "userId",
          "resourceId",
          "resourceType",
          "roles",
          "createdAt",
          "updatedAt",
          "id"
        ]
      },
      "IndexPaginatedMessages": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          }
        ]
      },
      "CursorPaginatedMessages": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          },
          "prevCursor": {
            "type": "string",
            "nullable": true
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "totalResults": {
            "type": "integer"
          },
          "totalPreviousResults": {
            "type": "integer"
          },
          "totalNextResults": {
            "type": "integer"
          }
        }
      },
      "IndexPaginatedReplies": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ChatMessage"
                }
              }
            }
          }
        ]
      },
      "CursorPaginatedReplies": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          },
          "prevCursor": {
            "type": "string",
            "nullable": true
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "totalResults": {
            "type": "integer"
          },
          "totalPreviousResults": {
            "type": "integer"
          },
          "totalNextResults": {
            "type": "integer"
          }
        }
      },
      "IndexPaginatedChatMembers": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ChatMember"
                }
              }
            }
          }
        ]
      },
      "CursorPaginatedChatMembers": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChatMember"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          },
          "prevCursor": {
            "type": "string",
            "nullable": true
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "totalResults": {
            "type": "integer"
          },
          "totalPreviousResults": {
            "type": "integer"
          },
          "totalNextResults": {
            "type": "integer"
          }
        }
      },
      "Convo": {
        "type": "object",
        "description": "A video/audio conversation. Participant, starter, chat and scope fields are populated on every route that returns a convo.",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/UUID"
          },
          "convoType": {
            "type": "string",
            "enum": [
              "video",
              "audio"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "completed",
              "cancelled"
            ]
          },
          "subject": {
            "type": "string",
            "description": "Optional subject. Omitted when not set."
          },
          "notes": {
            "type": "string",
            "description": "Optional notes. Omitted when not set."
          },
          "chatId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "ID of the chat the convo belongs to"
          },
          "chat": {
            "description": "The populated chat (Chat, ChatMember or ChatSubmission depending on `chatType`).",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Chat"
              },
              {
                "$ref": "#/components/schemas/ChatMember"
              },
              {
                "$ref": "#/components/schemas/ChatSubmission"
              }
            ]
          },
          "chatType": {
            "type": "string",
            "enum": [
              "chat",
              "chatMember",
              "chatSubmission"
            ],
            "description": "Internal chat model name (request bodies use `topic` / `member` / `submission`)."
          },
          "messageId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "ID of the system message created when the convo started"
          },
          "message": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "Alias of `messageId`"
          },
          "dailyRoomName": {
            "type": "string",
            "description": "Daily.co room name"
          },
          "dailyRoomUrl": {
            "type": "string",
            "description": "Full URL to join the Daily.co room"
          },
          "dailyRoomConfig": {
            "type": "object",
            "description": "Daily.co room configuration used when the room was created"
          },
          "startedById": {
            "$ref": "#/components/schemas/UUID"
          },
          "startedBy": {
            "description": "User who started the convo (populated)",
            "allOf": [
              {
                "$ref": "#/components/schemas/PublicUser"
              }
            ]
          },
          "activeParticipants": {
            "type": "array",
            "description": "Users currently in the convo (populated)",
            "items": {
              "$ref": "#/components/schemas/PublicUser"
            }
          },
          "allParticipants": {
            "type": "array",
            "description": "All users who have joined at any point (populated)",
            "items": {
              "$ref": "#/components/schemas/PublicUser"
            }
          },
          "startedAt": {
            "type": "integer",
            "format": "int64",
            "description": "Timestamp (ms) the convo started"
          },
          "endedAt": {
            "type": "integer",
            "format": "int64",
            "nullable": true,
            "description": "Timestamp (ms) the convo completed or was cancelled"
          },
          "durationMinutes": {
            "type": "integer",
            "nullable": true,
            "description": "Duration in minutes, set on completion"
          },
          "recordings": {
            "type": "array",
            "description": "Daily.co recordings attached to the convo",
            "items": {
              "type": "object"
            }
          },
          "transcripts": {
            "type": "array",
            "description": "Daily.co transcripts attached to the convo",
            "items": {
              "type": "object"
            }
          },
          "participantSessions": {
            "type": "array",
            "description": "Per-participant session records appended from Daily.co webhooks",
            "items": {
              "type": "object"
            }
          },
          "billedAtVideo": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "billedCreditsVideo": {
            "type": "integer",
            "nullable": true
          },
          "billedAtRecording": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "billedCreditsRecording": {
            "type": "integer",
            "nullable": true
          },
          "inheritanceId": {
            "$ref": "#/components/schemas/UUID"
          },
          "scopeId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "description": "ID of the project scope (when the chat belongs to a project)"
          },
          "scopeType": {
            "type": "string",
            "enum": [
              "project"
            ],
            "nullable": true
          },
          "scopeVisibility": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "creator",
                "reviewer"
              ]
            }
          },
          "createdAt": {
            "type": "integer",
            "format": "int64"
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "MembershipReportWithTotals": {
        "allOf": [
          {
            "$ref": "#/components/schemas/MembershipReport"
          },
          {
            "type": "object",
            "properties": {
              "totalCreators": {
                "type": "integer",
                "description": "Number of matching members holding a creator role."
              },
              "totalReviewers": {
                "type": "integer",
                "description": "Number of matching members holding a reviewer role."
              }
            }
          }
        ]
      },
      "CursorPaginatedNotifications": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Notification"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          },
          "prevCursor": {
            "type": "string",
            "nullable": true
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "totalResults": {
            "type": "integer"
          },
          "totalPreviousResults": {
            "type": "integer"
          },
          "totalNextResults": {
            "type": "integer"
          }
        }
      },
      "IndexPaginatedNotifications": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Notification"
                }
              },
              "hasNextPage": {
                "type": "boolean"
              },
              "hasPrevPage": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "IndexPaginatedNewNotifications": {
        "allOf": [
          {
            "$ref": "#/components/schemas/IndexPaginatedNotifications"
          },
          {
            "type": "object",
            "properties": {
              "newNotificationCount": {
                "type": "integer",
                "description": "Count of notifications newer than the caller's last-seen record for these channels/types (independent of `limit`)."
              },
              "createdBefore": {
                "type": "integer",
                "format": "int64",
                "description": "Timestamp (ms) used as the upper bound of the query."
              },
              "lastSeen": {
                "type": "integer",
                "format": "int64",
                "nullable": true,
                "description": "The caller's previous last-seen timestamp for these channels/types, or null if there was no record."
              },
              "updatedLastSeen": {
                "type": "integer",
                "format": "int64",
                "nullable": true,
                "description": "New last-seen timestamp written by this call when `updateLastSeen` is true; otherwise null."
              }
            }
          }
        ]
      },
      "Submissions": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          }
        ]
      },
      "CursorPaginatedChatMessagesWithChat": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CursorPaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ChatMessage"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "chat": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/Chat"
                            }
                          ],
                          "description": "Parent chat. Populated on the highlighted-messages listings."
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        ]
      },
      "CursorPaginatedSubmissions": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CursorPaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ChatSubmission"
                }
              }
            }
          }
        ]
      },
      "AssetWithChat": {
        "description": "An asset as returned by the feed / asset listings. When chats are included, the entry of `chats` for the requested visibility is populated with the chat and its recent messages instead of the bare chat ID.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/Asset"
          },
          {
            "type": "object",
            "properties": {
              "chats": {
                "type": "object",
                "properties": {
                  "creator": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/UUID"
                      },
                      {
                        "$ref": "#/components/schemas/Chat"
                      }
                    ]
                  },
                  "reviewer": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/UUID"
                      },
                      {
                        "$ref": "#/components/schemas/Chat"
                      }
                    ]
                  }
                }
              }
            }
          }
        ]
      },
      "Feed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AssetWithChat"
                }
              }
            }
          }
        ]
      },
      "SearchResult": {
        "type": "object",
        "description": "A single hit from the project search endpoint. The `record` field carries the same populated shape the content type's native list view returns (Asset / ChatMessage / Task).\n",
        "required": [
          "id",
          "contentType",
          "createdAt",
          "relevance"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/UUID"
          },
          "contentType": {
            "type": "string",
            "enum": [
              "asset",
              "chatMessage",
              "task"
            ]
          },
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Asset.name or Task.subject. Null for chat messages — use `snippet`."
          },
          "snippet": {
            "type": "string",
            "nullable": true,
            "description": "ts_headline excerpt with `<mark>...</mark>` highlights around matched terms. Null for assets."
          },
          "creatorId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "nullable": true,
            "description": "Authoring user. Asset.creatorId / ChatMessage.authorId / Task.creatorId."
          },
          "chatId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UUID"
              }
            ],
            "nullable": true,
            "description": "Set only on chatMessage hits — the parent chat's UUID."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "relevance": {
            "type": "number",
            "format": "float",
            "description": "Relevance score; higher means a better match."
          },
          "record": {
            "nullable": true,
            "description": "Fully-populated record, shaped per the content type's list view.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Asset"
              },
              {
                "$ref": "#/components/schemas/ChatMessage"
              },
              {
                "$ref": "#/components/schemas/Task"
              }
            ]
          }
        }
      },
      "SearchResults": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SearchResult"
                }
              }
            }
          }
        ]
      },
      "UpdateLogoResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AssetAndSignedLink"
          },
          {
            "type": "object",
            "properties": {
              "project": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Project"
                  }
                ],
                "description": "The project with its `logo` reference updated."
              }
            }
          }
        ]
      },
      "DeleteImpact": {
        "type": "object",
        "description": "What a delete would reach beyond the rows named in the request. Computed from the same cascade the delete runs.\n",
        "properties": {
          "selectedCount": {
            "type": "integer",
            "description": "How many of the requested paths resolved to a row."
          },
          "affectedAssetCount": {
            "type": "integer",
            "description": "Distinct assets that would lose a reference."
          },
          "counts": {
            "type": "object",
            "description": "Reference count per collection. Only non-empty collections appear.",
            "properties": {
              "reviewer": {
                "type": "integer"
              },
              "submission": {
                "type": "integer"
              },
              "public": {
                "type": "integer"
              },
              "other": {
                "type": "integer"
              }
            }
          },
          "references": {
            "type": "array",
            "description": "The secondary references that would be removed — the reviewer, submission and public-release copies. Does not include the rows the caller listed.\n",
            "items": {
              "type": "object",
              "properties": {
                "fileSystemId": {
                  "type": "string",
                  "format": "uuid"
                },
                "assetId": {
                  "type": "string",
                  "format": "uuid"
                },
                "assetName": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "reviewer",
                    "submission",
                    "public",
                    "other"
                  ]
                },
                "path": {
                  "type": "string"
                },
                "itemPath": {
                  "type": "string"
                },
                "label": {
                  "type": "string",
                  "description": "Human-readable location, e.g. \"Submission / Round 2\"."
                }
              }
            }
          }
        },
        "required": [
          "selectedCount",
          "affectedAssetCount",
          "counts",
          "references"
        ]
      },
      "PublishItemError": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Error code, e.g. `resourcesNotFound`, `publisherRequired`."
          },
          "message": {
            "type": "string"
          },
          "data": {
            "description": "Error-specific payload (for example the ids that were not found)."
          }
        }
      },
      "PublishItemsResponse": {
        "type": "object",
        "properties": {
          "published": {
            "type": "array",
            "description": "The submitted items that were published (assets and folders).",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/Asset"
                },
                {
                  "$ref": "#/components/schemas/Folder"
                }
              ]
            }
          },
          "totalPublished": {
            "type": "integer",
            "description": "Total items published, including assets inside published folders."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublishItemError"
            }
          }
        },
        "required": [
          "published",
          "totalPublished",
          "errors"
        ]
      },
      "UnpublishItemsResponse": {
        "type": "object",
        "properties": {
          "unpublished": {
            "type": "array",
            "description": "The assets that were unpublished.",
            "items": {
              "$ref": "#/components/schemas/Asset"
            }
          },
          "totalUnpublished": {
            "type": "integer",
            "description": "Total items unpublished, including removed reviewer file-system entries."
          },
          "removedPaths": {
            "type": "array",
            "description": "Reviewer-tree paths whose entries were removed (submitted items only).",
            "items": {
              "type": "string"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublishItemError"
            }
          }
        },
        "required": [
          "unpublished",
          "totalUnpublished",
          "removedPaths",
          "errors"
        ]
      },
      "FileSystemItem": {
        "type": "object",
        "description": "A FileSystem entry — one placement of an asset or folder at a path — with its populated resource.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "asset",
              "folder"
            ]
          },
          "resourceName": {
            "type": "string"
          },
          "resourceSlug": {
            "type": "string",
            "nullable": true
          },
          "resourceCreatorId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceTags": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "resourceMediaType": {
            "type": "string",
            "description": "Media type of the resource (`folder` for folders)."
          },
          "resourceStatus": {
            "type": "string",
            "enum": [
              "active",
              "pendingDelete"
            ]
          },
          "resourceCreatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "sizeInBytes": {
            "type": "integer",
            "format": "int64"
          },
          "path": {
            "type": "string",
            "description": "Parent path of the entry, e.g. `project/{projectId}/creator/Renders`."
          },
          "itemPath": {
            "type": "string",
            "nullable": true,
            "description": "Full path of the item including its own slug."
          },
          "pathType": {
            "type": "string",
            "nullable": true,
            "description": "First segment of `path` (`project`, `submission`, `public`, ...)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "resource": {
            "description": "The populated asset or folder. On submission listings an asset's `chats.reviewer` is populated with the reviewer chat; on project and public listings `chats` holds bare chat IDs.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Asset"
              },
              {
                "$ref": "#/components/schemas/Folder"
              }
            ]
          }
        }
      },
      "FileSystemItems": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/FileSystemItem"
                }
              }
            }
          }
        ]
      },
      "CursorPaginatedFileSystemItems": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CursorPaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/FileSystemItem"
                }
              }
            }
          }
        ]
      },
      "FileOperationResponse": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "description": "Number of items successfully processed",
            "example": 1
          },
          "virtualDestination": {
            "type": "boolean",
            "description": "Present (true) when a move targeted a virtual folder (`Public/...`, `Submission/...`, `Review/...`) and was performed as a copy / publish instead."
          },
          "published": {
            "type": "boolean",
            "description": "Present (true) when a move onto `Review/...` published the items."
          },
          "errors": {
            "type": "array",
            "description": "Per-item publish errors; present only when `published` is true.",
            "items": {
              "$ref": "#/components/schemas/PublishItemError"
            }
          }
        },
        "required": [
          "count"
        ]
      },
      "AddItemsToPublicFileSystemResponse": {
        "type": "object",
        "properties": {
          "publicFileSystem": {
            "$ref": "#/components/schemas/PublicFileSystem"
          },
          "itemsAdded": {
            "type": "integer",
            "description": "Number of file-system entries copied into the release."
          }
        },
        "required": [
          "publicFileSystem",
          "itemsAdded"
        ]
      },
      "PublicFileSystem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the public file system"
          },
          "ownerResourceType": {
            "type": "string",
            "enum": [
              "project"
            ],
            "description": "Type of the owning resource."
          },
          "ownerResourceId": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the owning project"
          },
          "token": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9]{10}$",
            "description": "Public access token"
          },
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Title of the public file system"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Description of the public file system"
          },
          "inventory": {
            "type": "object",
            "nullable": true,
            "description": "Denormalized count of items in the release keyed by media type (folders count under `folder`).",
            "additionalProperties": {
              "type": "integer"
            },
            "example": {
              "image": 12,
              "video": 2,
              "folder": 3
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expired",
              "disabled",
              "unreleased",
              "error"
            ],
            "description": "Status of the public file system. `unreleased` is a staged release that isn't yet externally accessible; `error` is transient while the release's items are being copied."
          },
          "expires": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Expiration date and time of the public file system, or `null` when it never expires."
          },
          "hideCreators": {
            "type": "boolean",
            "description": "Whether authorship is repressed on the external public API. When `true`, the `/public/{token}*` endpoints omit both this record's `creator` and every listed item's creator.\n"
          },
          "allowAnonymousComments": {
            "type": "boolean",
            "description": "Whether unauthenticated visitors may comment on this release's chats by supplying a display name."
          },
          "creatorId": {
            "type": "string",
            "format": "uuid"
          },
          "creator": {
            "allOf": [
              {
                "$ref": "#/components/schemas/User"
              }
            ],
            "description": "User who created the public file system (\"Shared by\"). Omitted from the external `/public/{token}` response when `hideCreators` is true.\n"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Creation date and time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Last update date and time"
          }
        },
        "required": [
          "id",
          "ownerResourceType",
          "ownerResourceId",
          "token",
          "status",
          "hideCreators",
          "allowAnonymousComments",
          "creatorId",
          "createdAt",
          "updatedAt"
        ]
      },
      "PublicFileSystems": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          }
        ]
      },
      "CursorPaginatedPublicFileSystems": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CursorPaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PublicFileSystem"
                }
              }
            }
          }
        ]
      },
      "PublicAuditAsset": {
        "type": "object",
        "description": "A project asset that is or has been publicly exposed.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Asset ID."
          },
          "name": {
            "type": "string",
            "description": "Asset name."
          },
          "mediaType": {
            "type": "string",
            "description": "Media type of the asset (e.g., `image`, `video`)."
          },
          "thumbnail": {
            "nullable": true,
            "type": "object",
            "description": "Thumbnail file reference, or `null` when no thumbnail is available.",
            "properties": {
              "keyPath": {
                "type": "string",
                "description": "Storage key path for the thumbnail file."
              }
            }
          },
          "creator": {
            "allOf": [
              {
                "$ref": "#/components/schemas/User"
              }
            ],
            "nullable": true,
            "description": "User who created the asset, populated with public profile fields and avatar."
          },
          "publicFileSystems": {
            "type": "array",
            "description": "All public file systems that have included this asset (ordered by most recent first).",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "title": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "active",
                    "expired",
                    "disabled",
                    "unreleased",
                    "error"
                  ]
                }
              },
              "required": [
                "id",
                "title",
                "status"
              ]
            }
          },
          "publicLinkCount": {
            "type": "integer",
            "description": "All-time count of `PublicAssetLink` records for this asset, regardless of status."
          },
          "hasActivePublicLink": {
            "type": "boolean",
            "description": "Denormalized flag — `true` when the asset has at least one active public download link."
          },
          "everPublic": {
            "type": "boolean",
            "description": "One-way flag — `true` once the asset has been publicly exposed at any point."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the asset was created."
          }
        },
        "required": [
          "id",
          "name",
          "mediaType",
          "thumbnail",
          "creator",
          "publicFileSystems",
          "publicLinkCount",
          "hasActivePublicLink",
          "everPublic",
          "createdAt"
        ]
      },
      "PublicAuditAssets": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PublicAuditAsset"
                }
              }
            }
          }
        ]
      },
      "PublicCursorPaginatedFileSystemItems": {
        "description": "Cursor-paginated file-system items (returned when `paginate=cursor`). Item shape matches `FileSystemItems.results[]`.",
        "allOf": [
          {
            "$ref": "#/components/schemas/CursorPaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "resourceId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "resourceType": {
                      "type": "string",
                      "enum": [
                        "asset",
                        "folder"
                      ]
                    },
                    "resourceName": {
                      "type": "string"
                    },
                    "path": {
                      "type": "string"
                    },
                    "resourceCreatedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "resource": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Asset"
                        },
                        {
                          "$ref": "#/components/schemas/Folder"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "PublicAssetLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the public asset link."
          },
          "token": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9]{10}$",
            "description": "The 10-character alphanumeric download token.",
            "example": "Ab3dEf9HiJ"
          },
          "assetId": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the linked asset."
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the project the asset belongs to."
          },
          "creatorId": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the user who created the link."
          },
          "expires": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Expiration timestamp. Null if the link does not expire."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "expired"
            ],
            "description": "Current status of the link."
          },
          "mode": {
            "type": "string",
            "enum": [
              "download",
              "embed",
              "embed-download"
            ],
            "description": "Capability mode for the link.\n- `download` — only the `/download` endpoint is allowed.\n- `embed` — only the `/embed-files` endpoint is allowed.\n- `embed-download` — both endpoints are allowed (default; backward-compatible).\n",
            "example": "embed-download"
          },
          "publicUrl": {
            "type": "string",
            "description": "The full public download page URL.",
            "example": "https://app.nurama.com/public-download/Ab3dEf9HiJ"
          },
          "isExpired": {
            "type": "boolean",
            "description": "Whether the link has passed its expiration date."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "token",
          "assetId",
          "projectId",
          "creatorId",
          "status",
          "publicUrl"
        ]
      },
      "ChartDataEntry": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "description": "Period label formatted for the aggregation period (UTC), e.g. `YYYY-MM-DD` for `day`.",
            "example": "2026-09-20"
          },
          "averageSizeInBytes": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "ChartDataArray": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/ChartDataEntry"
        }
      },
      "StorageRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "user",
              "workspace",
              "project",
              "chat",
              "asset"
            ]
          },
          "sizeInBytes": {
            "type": "integer",
            "minimum": 0
          },
          "triggerEvent": {
            "type": "string",
            "description": "What produced this record, e.g. `fileUpload`, `fileDelete`, `assetDelete`, `softAudit`, `hardAudit`."
          },
          "triggerResourceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TaskRelationRecord": {
        "type": "object",
        "description": "A task relation row as stored.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "chat",
              "chatMessage"
            ]
          },
          "creatorId": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "integer",
            "format": "int64"
          },
          "updatedAt": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "TaskRelation": {
        "allOf": [
          {
            "$ref": "#/components/schemas/TaskRelationRecord"
          },
          {
            "type": "object",
            "properties": {
              "relatedBy": {
                "description": "Public user who created the relation (falls back to the creator's ID if the user cannot be loaded)",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/PublicUser"
                  },
                  {
                    "type": "string",
                    "format": "uuid"
                  }
                ]
              },
              "chat": {
                "type": "object",
                "nullable": true,
                "description": "Parent chat metadata (populated for both chat and chatMessage relations)"
              },
              "asset": {
                "type": "object",
                "nullable": true,
                "description": "Populated when chat.topicType is 'asset'",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string"
                  },
                  "mediaType": {
                    "type": "string"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              },
              "submission": {
                "type": "object",
                "nullable": true,
                "description": "Populated when chat.chatType is 'submission'",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "subject": {
                    "type": "string"
                  }
                }
              },
              "publicLink": {
                "type": "object",
                "nullable": true,
                "description": "Populated when the chat belongs to a public release",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              "message": {
                "type": "object",
                "nullable": true,
                "description": "Populated when resourceType is 'chatMessage'",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "chatId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "content": {
                    "type": "string"
                  },
                  "authorId": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true
                  },
                  "author": {
                    "nullable": true,
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/PublicUser"
                      }
                    ]
                  },
                  "mentions": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/PublicUser"
                    }
                  },
                  "assetMentions": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "folderMentions": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "createdAt": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              }
            }
          }
        ]
      },
      "PaginatedTaskRelations": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedResult"
          },
          {
            "type": "object",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/TaskRelation"
                }
              },
              "hasNextPage": {
                "type": "boolean"
              },
              "hasPrevPage": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "TaskEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "actorId": {
            "type": "string",
            "format": "uuid"
          },
          "eventType": {
            "type": "string",
            "enum": [
              "created",
              "addedToBoard",
              "moved",
              "assigned",
              "unassigned",
              "updated",
              "removedFromBoard",
              "linked",
              "unlinked",
              "followed",
              "unfollowed"
            ],
            "description": "Event type. Intended as the key for a localised message template."
          },
          "detail": {
            "type": "object",
            "description": "Structured context for rendering the event (e.g. column names). Contents vary by `eventType`.",
            "additionalProperties": true,
            "example": {
              "toColumnName": "In Progress",
              "fromColumnName": "Backlog"
            }
          },
          "actor": {
            "type": "object",
            "description": "Public user who performed the action (with avatar)",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "displayName": {
                "type": "string"
              },
              "firstName": {
                "type": "string"
              },
              "lastName": {
                "type": "string"
              },
              "color": {
                "type": "string"
              },
              "avatar": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "functionType": {
                          "type": "string"
                        },
                        "keyPath": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaginatedTaskEvents": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskEvent"
            }
          },
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "totalPages": {
            "type": "integer"
          },
          "totalResults": {
            "type": "integer"
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "hasPrevPage": {
            "type": "boolean"
          }
        }
      },
      "CursorPaginatedTasks": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Task"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Cursor to get the next page of results"
          },
          "prevCursor": {
            "type": "string",
            "description": "Cursor to get the previous page of results"
          },
          "hasNextPage": {
            "type": "boolean",
            "description": "Whether there are more results after this page"
          },
          "hasPrevPage": {
            "type": "boolean",
            "description": "Whether there are more results before this page"
          },
          "counts": {
            "type": "object",
            "description": "Only included when includeCounts=true",
            "properties": {
              "totalDocs": {
                "type": "number",
                "description": "Total number of documents matching the query"
              },
              "totalDocsAfter": {
                "type": "number",
                "description": "Total number of documents after the current page"
              },
              "totalDocsBefore": {
                "type": "number",
                "description": "Total number of documents before the current page"
              }
            }
          }
        }
      },
      "WebhookEventName": {
        "type": "string",
        "enum": [
          "task.created",
          "task.updated",
          "task.deleted",
          "chat.message.created",
          "asset.published",
          "webhook.test"
        ]
      },
      "WebhookSubscription": {
        "type": "object",
        "description": "A webhook subscription. The signing secret is never included.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "workspaceId": {
            "type": "string",
            "format": "uuid"
          },
          "appId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Reserved for the future OAuth/Apps track; always null today."
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventName"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "failedOut"
            ]
          },
          "failedOutAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "failedOutReason": {
            "type": "string",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdById": {
            "type": "string",
            "format": "uuid"
          },
          "lastDeliveryAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastSuccessAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastFailureAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookAttempt": {
        "type": "object",
        "description": "One delivery attempt of one notification to one subscription.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "notificationId": {
            "type": "string",
            "format": "uuid"
          },
          "subscriptionId": {
            "type": "string",
            "format": "uuid"
          },
          "attempt": {
            "type": "integer",
            "description": "1-based attempt number within the retry sequence."
          },
          "maxAttempts": {
            "type": "integer",
            "default": 8
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "inflight",
              "succeeded",
              "failed",
              "dlq"
            ]
          },
          "signatureV1": {
            "type": "string",
            "description": "Signature value sent (or to be recomputed) for this attempt. Debugging aid."
          },
          "scheduledAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "deliveredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "nextRetryAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "responseCode": {
            "type": "integer",
            "nullable": true
          },
          "responseBody": {
            "type": "string",
            "nullable": true
          },
          "responseHeaders": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          },
          "errorMessage": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WorkspaceWithRoles": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Workspace"
          },
          {
            "type": "object",
            "properties": {
              "roles": {
                "type": "array",
                "description": "The calling user's roles on this workspace.",
                "items": {
                  "type": "string"
                },
                "example": [
                  "workspaceOwner"
                ]
              },
              "capabilities": {
                "type": "array",
                "description": "Capability tokens available to the workspace under its current plan and opt-in settings (e.g. `boards`, `ai`, `convos`, `publicSharing`). Empty when the workspace has no active subscription. Not present on the create response.",
                "items": {
                  "type": "string"
                }
              },
              "settings": {
                "type": "object",
                "additionalProperties": true,
                "description": "Workspace settings JSON (see `PUT /workspaces/{workspaceId}/settings/{name}`)."
              }
            }
          }
        ]
      },
      "WorkspaceImageUploadResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AssetAndSignedLink"
          },
          {
            "type": "object",
            "properties": {
              "workspace": {
                "$ref": "#/components/schemas/Workspace"
              }
            }
          }
        ]
      },
      "IdNameOrScratchIdNameUnion": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Client-side counter used to match request items with `attachmentData` items.",
                "example": 1
              },
              "name": {
                "type": "string",
                "description": "File name with extension.",
                "example": "photo.jpg"
              },
              "sizeInMB": {
                "type": "number",
                "example": 1.2
              },
              "checksum": {
                "type": "string",
                "description": "MD5 or SHA-256 hash of the file."
              }
            },
            "required": [
              "id",
              "name",
              "sizeInMB",
              "checksum"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "scratchId": {
                "type": "string",
                "format": "uuid",
                "description": "ID of the staged Scratch object to promote."
              },
              "name": {
                "type": "string",
                "description": "Optional display name for the promoted asset."
              }
            },
            "required": [
              "scratchId"
            ],
            "additionalProperties": false
          }
        ]
      },
      "SharedColorBlurOffsetX": {
        "type": "object",
        "properties": {
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "rgba(0,0,0,0.3)"
          },
          "blur": {
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "description": "Shadow blur radius in pixels",
            "default": 5
          },
          "offsetX": {
            "type": "number",
            "minimum": -10,
            "maximum": 10,
            "description": "Shadow X offset as percentage of media width",
            "default": 0.2
          },
          "offsetY": {
            "type": "number",
            "minimum": -10,
            "maximum": 10,
            "description": "Shadow Y offset as percentage of media height",
            "default": 0.2
          }
        },
        "additionalProperties": false
      },
      "SharedUrlTitleDescription": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "The original URL."
          },
          "title": {
            "type": "string",
            "maxLength": 200,
            "nullable": true,
            "description": "Page title from og:title or <title> tag."
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "nullable": true,
            "description": "Page description from og:description or meta description."
          },
          "image": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "nullable": true,
            "description": "Image URL from og:image."
          },
          "siteName": {
            "type": "string",
            "maxLength": 100,
            "nullable": true,
            "description": "Site name from og:site_name."
          },
          "favicon": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "nullable": true,
            "description": "Favicon URL."
          },
          "signature": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64,
            "description": "HMAC-SHA256 signature for anti-spoofing verification (transient, not persisted)."
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false,
        "description": "Rich link preview metadata extracted from a URL (Open Graph / meta tags)."
      },
      "SharedIdFirstNameLastName": {
        "type": "object",
        "properties": {
          "id": {
            "type": "number",
            "enum": [
              1,
              -1
            ],
            "description": "Sort by membership ID (1 for ascending, -1 for descending)."
          },
          "firstName": {
            "type": "number",
            "enum": [
              1,
              -1
            ],
            "description": "Sort by user's first name (1 for ascending, -1 for descending)."
          },
          "lastName": {
            "type": "number",
            "enum": [
              1,
              -1
            ],
            "description": "Sort by user's last name (1 for ascending, -1 for descending)."
          },
          "displayName": {
            "type": "number",
            "enum": [
              1,
              -1
            ],
            "description": "Sort by user's display name (1 for ascending, -1 for descending)."
          },
          "createdAt": {
            "type": "number",
            "enum": [
              1,
              -1
            ],
            "description": "Sort by membership creation date (1 for ascending, -1 for descending)."
          },
          "updatedAt": {
            "type": "number",
            "enum": [
              1,
              -1
            ],
            "description": "Sort by membership update date (1 for ascending, -1 for descending)."
          },
          "isBillable": {
            "type": "number",
            "enum": [
              1,
              -1
            ],
            "description": "Sort by billable status (1 for ascending, -1 for descending)."
          }
        },
        "additionalProperties": false,
        "description": "Sorting criteria for the membership records."
      },
      "Shared5fee7073": {
        "type": "array",
        "items": {
          "type": "string",
          "enum": [
            "test",
            "notificationUpdate",
            "workspaceCreate",
            "workspaceUpdate",
            "workspaceDelete",
            "workspaceLogoUpdate",
            "projectCreate",
            "projectUpdate",
            "projectDelete",
            "projectLogoUpdate",
            "projectAssetsPublish",
            "projectAssetsUnpublish",
            "projectItemsPublish",
            "projectItemsUnpublish",
            "projectGroupItemPublish",
            "assetNameChange",
            "assetPublish",
            "assetUnpublish",
            "assetTag",
            "assetUntag",
            "publicAssetLinkCreate",
            "assetDelete",
            "assetStatusUpdate",
            "assetPostProcessUpdate",
            "assetFileUpdate",
            "assetGroupUploadComplete",
            "uploadPushSummary",
            "publishPushSummary",
            "submissionPushSummary",
            "memberJoinPush",
            "chatTopicChatCreate",
            "chatMemberChatCreate",
            "chatUpdateSubject",
            "chatMemberUpdate",
            "chatDelete",
            "chatMemberDelete",
            "chatMemberArchive",
            "chatMemberUnarchive",
            "chatCreateMessage",
            "chatReviseMessage",
            "chatRefreshMessage",
            "chatMention",
            "chatDeleteMessage",
            "chatRemoveAttachment",
            "chatHighlightMessage",
            "chatFollow",
            "chatUnfollow",
            "folderCreate",
            "folderUpdate",
            "folderDelete",
            "folderPublish",
            "folderTag",
            "folderUntag",
            "inviteCreate",
            "inviteCancel",
            "inviteAccept",
            "membershipDelete",
            "membershipAddRole",
            "membershipRemoveRole",
            "membershipLeaveResource",
            "workspaceStorageLimitWarning",
            "userEmailVerify",
            "userSelfUpdate",
            "userPublicUpdate",
            "userDeleted",
            "userAvatarUpdate",
            "botCreate",
            "webhookTest",
            "logoUpdate",
            "iconUpdate",
            "subscriptionCreate",
            "subscriptionUpdate",
            "taskAcknowledged",
            "taskStatusUpdate",
            "taskCreate",
            "taskFollow",
            "taskUnfollow",
            "notificationUpdateLastSeen",
            "submissionCreate",
            "submissionUpdate",
            "submissionTag",
            "submissionUntag",
            "aiChatTopicCreate",
            "aiChatTopicUpdate",
            "aiChatMessageCreate",
            "tagCreate",
            "tagUpdate",
            "tagDelete",
            "fileSystemCreate",
            "fileSystemMove",
            "fileSystemCopy",
            "fileSystemDelete",
            "fileSystemPublish",
            "fileSystemUnpublish",
            "publicFileSystemCreate",
            "publicFileSystemUpdate",
            "publicFileSystemDelete",
            "settingsUpdate",
            "convoStart",
            "convoJoin",
            "convoLeave",
            "convoComplete",
            "convoUpdate",
            "convoDelete",
            "convoParticipantJoined",
            "convoParticipantLeft",
            "convoHandover",
            "boardCreate",
            "boardUpdate",
            "boardDelete",
            "boardTaskCreate",
            "boardTaskMove",
            "boardTaskAssign",
            "boardColumnAdd",
            "boardColumnUpdate",
            "boardColumnDelete",
            "boardColumnReorder",
            "boardTaskAdd",
            "boardTaskRemove",
            "boardTaskUpdate",
            "boardTaskLink",
            "boardTaskUnlink",
            "boardTaskRelationAdd",
            "boardTaskRelationRemove",
            "boardFollow",
            "boardUnfollow"
          ]
        },
        "uniqueItems": true,
        "description": "Notification types to include (notification `type` names, e.g. `chatMessageCreate`; see the WebSocket event reference)."
      },
      "SharedIdNameChecksum": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "maximum": 10,
            "default": 1,
            "description": "Requester-side correlation id. Any integer up to 10."
          },
          "name": {
            "type": "string",
            "pattern": "^(?!.*\\.(exe|bat|com|msi|vbs|ps1|app|command|tool|sh|bin|run|jar|py|pl|rb)$).*",
            "description": "File name including a valid extension. Executable / restricted extensions are rejected.",
            "example": "logo.png"
          },
          "checksum": {
            "type": "string",
            "pattern": "^[a-f0-9]{32,64}$",
            "description": "MD5 or SHA-256 hash of the file content."
          },
          "sizeInMB": {
            "type": "number",
            "minimum": 0,
            "x-exclusiveMinimum": true,
            "description": "File size in MB (positive). Used to size the signed upload."
          }
        },
        "required": [
          "name",
          "checksum",
          "sizeInMB"
        ],
        "additionalProperties": false,
        "description": "A single logo file to upload. Unlike `UploadFile`, `basePath` is not accepted here."
      },
      "SharedNameColorBasePath": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "Name of the folder (HTML is sanitised). Reserved names `Review`, `Public` and `Submission` are rejected.",
            "example": "Renders"
          },
          "color": {
            "type": "string",
            "enum": [
              "#37474F",
              "#FF5722",
              "#2962FF",
              "#33691E",
              "#00796B",
              "#455A64",
              "#2979FF",
              "#827717",
              "#7986CB",
              "#8E24AA",
              "#9575CD",
              "#BF360C",
              "#01579B",
              "#EF6C00",
              "#AA00FF",
              "#F44336",
              "#7C4DFF",
              "#E65100",
              "#8D6E63",
              "#283593",
              "#607D8B",
              "#009688",
              "#FF5252",
              "#03A9F4",
              "#C2185B",
              "#00ACC1",
              "#E91E63",
              "#5D4037",
              "#78909C",
              "#1E88E5",
              "#D500F9",
              "#7E57C2",
              "#5C6BC0",
              "#558B2F",
              "#2E7D32",
              "#F50057",
              "#004D40",
              "#0D47A1",
              "#C51162",
              "#D50000",
              "#6200EA",
              "#00BCD4",
              "#0277BD"
            ],
            "description": "Hex color code for the folder. Must be one of the approved colours."
          },
          "basePath": {
            "type": "string",
            "description": "Folder path (relative to the tree root) the folder should be created in. Omit to create at the root.",
            "example": "Renders/2024"
          }
        },
        "additionalProperties": false
      },
      "SharedPaginateRecursiveSearchResourceIds": {
        "type": "object",
        "properties": {
          "paginate": {
            "type": "string",
            "default": "index",
            "enum": [
              "cursor",
              "index"
            ]
          },
          "recursiveSearch": {
            "type": "boolean",
            "description": "If true, includes items in every sub-folder beneath the path."
          },
          "resourceIds": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              {
                "type": "string",
                "format": "uuid"
              }
            ],
            "default": [],
            "description": "Filter by resource IDs. May not be combined with `resourceSlugs`."
          },
          "resourceSlugs": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^[a-z0-9-]+$"
                }
              },
              {
                "type": "string",
                "pattern": "^[a-z0-9-]+$"
              }
            ],
            "default": [],
            "description": "Filter by resource slugs. May not be combined with `resourceIds`."
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "asset",
              "folder"
            ]
          },
          "resourceTags": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              {
                "type": "string",
                "format": "uuid"
              }
            ],
            "default": [],
            "description": "Filter by tag IDs."
          },
          "creatorId": {
            "type": "string",
            "format": "uuid"
          },
          "mediaTypes": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio",
                    "folder",
                    "file"
                  ]
                }
              },
              {
                "type": "string",
                "enum": [
                  "image",
                  "video",
                  "audio",
                  "folder",
                  "file"
                ]
              }
            ],
            "default": []
          },
          "resourceStatus": {
            "type": "string",
            "default": "active",
            "enum": [
              "active",
              "pendingDelete"
            ]
          },
          "nameSearch": {
            "type": "string",
            "maxLength": 100
          },
          "sort": {
            "type": "object",
            "properties": {
              "id": {
                "type": "number",
                "enum": [
                  1,
                  -1
                ]
              },
              "name": {
                "type": "number",
                "enum": [
                  1,
                  -1
                ]
              },
              "createdAt": {
                "type": "number",
                "enum": [
                  1,
                  -1
                ]
              },
              "updatedAt": {
                "type": "number",
                "enum": [
                  1,
                  -1
                ]
              },
              "mediaType": {
                "type": "number",
                "enum": [
                  1,
                  -1
                ]
              },
              "status": {
                "type": "number",
                "enum": [
                  1,
                  -1
                ]
              },
              "sizeInBytes": {
                "type": "number",
                "enum": [
                  1,
                  -1
                ]
              }
            },
            "additionalProperties": false,
            "default": {
              "id": -1
            }
          },
          "limit": {
            "type": "number",
            "minimum": 1,
            "maximum": 100,
            "default": 10
          },
          "cursor": {
            "type": "string",
            "x-conditionally-required": true
          },
          "paginateReverse": {
            "type": "boolean",
            "x-conditionally-required": true
          },
          "includeCounts": {
            "type": "boolean",
            "x-conditionally-required": true
          },
          "includeCursorRecord": {
            "type": "boolean",
            "x-conditionally-required": true
          },
          "startAt": {
            "type": "string",
            "format": "uuid",
            "x-conditionally-required": true
          },
          "includeStartAtRecord": {
            "type": "boolean",
            "x-conditionally-required": true
          },
          "page": {
            "type": "number",
            "minimum": 1,
            "x-conditionally-required": true
          }
        },
        "additionalProperties": false,
        "description": "Body form of the file-system listing options (same fields and rules as the GET query parameters). Cursor-only fields are rejected unless `paginate` is `cursor`; `page` is rejected unless `paginate` is `index`.\n"
      },
      "SharedIdNameChecksum2": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "maximum": 10,
            "default": 1,
            "description": "Client-side correlation id echoed back in the response."
          },
          "name": {
            "type": "string",
            "pattern": "\\.(jpg|jpeg|png|gif|tiff|bmp|webp|svg|heif|ico|raw|exr|heic|mp4|avi|mov|wmv|flv|mkv|webm|m4v|mpg|mpeg|rm|vob|3gp|ogv|ts|m2ts|hevc|divx)$",
            "description": "File name including an extension of a supported image or video type (e.g. `png`, `jpg`, `gif`, `webp`, `mp4`, `mov`).",
            "example": "avatar.png"
          },
          "checksum": {
            "type": "string",
            "pattern": "^[a-f0-9]{32,64}$",
            "description": "MD5 (32) or SHA-256 (64) hex hash of the file content."
          },
          "sizeInMB": {
            "type": "number",
            "minimum": 0,
            "x-exclusiveMinimum": true,
            "description": "File size in MB; used to determine how many signed upload links to generate."
          }
        },
        "required": [
          "name",
          "checksum",
          "sizeInMB"
        ],
        "additionalProperties": false
      },
      "SharedIdNameChecksum3": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "maximum": 10,
            "default": 1,
            "description": "Caller-chosen request item id, echoed back on the response."
          },
          "name": {
            "type": "string",
            "pattern": "\\.(jpg|jpeg|png|gif|tiff|bmp|webp|svg|heif|ico|raw|exr|heic|mp4|avi|mov|wmv|flv|mkv|webm|m4v|mpg|mpeg|rm|vob|3gp|ogv|ts|m2ts|hevc|divx)$",
            "description": "File name. Must end in a supported image or video extension.",
            "example": "logo.png"
          },
          "checksum": {
            "type": "string",
            "pattern": "^[a-f0-9]{32,64}$",
            "description": "MD5 (32 hex) or SHA-256 (64 hex) hash of the file content."
          },
          "sizeInMB": {
            "type": "number",
            "minimum": 0,
            "x-exclusiveMinimum": true,
            "description": "File size in MB; used to decide how many signed part-upload links to generate."
          }
        },
        "required": [
          "name",
          "checksum",
          "sizeInMB"
        ],
        "additionalProperties": false,
        "description": "Single-file upload descriptor for a workspace logo or icon. The server creates the asset and returns signed upload links."
      },
      "NestedDotAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "dot"
            ]
          },
          "coordinates": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "X coordinate as percentage (0-100)"
              },
              "y": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "Y coordinate as percentage (0-100)"
              }
            },
            "required": [
              "x",
              "y"
            ],
            "additionalProperties": false,
            "title": "AnnotationCoordinatesInput"
          },
          "radius": {
            "type": "number",
            "minimum": 5,
            "maximum": 100,
            "default": 10
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          }
        },
        "required": [
          "type",
          "coordinates"
        ],
        "additionalProperties": false,
        "title": "NestedDotAnnotationInput",
        "description": "Nested dot annotation"
      },
      "NestedShapeAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "rectangle",
              "circle",
              "triangle",
              "arrow",
              "line"
            ]
          },
          "coordinates": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "x": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "X coordinate as percentage (0-100)"
                },
                "y": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Y coordinate as percentage (0-100)"
                }
              },
              "required": [
                "x",
                "y"
              ],
              "additionalProperties": false,
              "title": "AnnotationCoordinatesInput"
            },
            "minItems": 2,
            "maxItems": 50,
            "description": "Array of coordinate points defining the shape"
          },
          "strokeColor": {
            "type": "string",
            "description": "Stroke color as hex code",
            "default": "#000000"
          },
          "fillColor": {
            "type": "string",
            "description": "Fill color as hex code (optional)",
            "default": null
          },
          "strokeWidth": {
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "description": "Stroke width in pixels",
            "default": 2
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          }
        },
        "required": [
          "type",
          "coordinates"
        ],
        "additionalProperties": false,
        "title": "NestedShapeAnnotationInput",
        "description": "Nested shape annotation"
      },
      "NestedTextAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "text"
            ]
          },
          "coordinates": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "X coordinate as percentage (0-100)"
              },
              "y": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "Y coordinate as percentage (0-100)"
              }
            },
            "required": [
              "x",
              "y"
            ],
            "additionalProperties": false,
            "title": "AnnotationCoordinatesInput"
          },
          "content": {
            "type": "string",
            "maxLength": 500,
            "description": "Text content to display"
          },
          "fontSize": {
            "type": "number",
            "minimum": 8,
            "maximum": 72,
            "description": "Font size in pixels",
            "default": 16
          },
          "fontFamily": {
            "type": "string",
            "default": "Arial",
            "enum": [
              "Arial",
              "Helvetica",
              "Times New Roman",
              "Courier New",
              "Georgia",
              "Verdana"
            ]
          },
          "fontWeight": {
            "type": "string",
            "default": "normal",
            "enum": [
              "normal",
              "bold"
            ]
          },
          "fontStyle": {
            "type": "string",
            "default": "normal",
            "enum": [
              "normal",
              "italic"
            ]
          },
          "textColor": {
            "type": "string",
            "description": "Text color as hex code",
            "default": "#000000"
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          }
        },
        "required": [
          "type",
          "coordinates",
          "content"
        ],
        "additionalProperties": false,
        "title": "NestedTextAnnotationInput",
        "description": "Nested text annotation"
      },
      "NestedPathAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "path"
            ]
          },
          "pathData": {
            "type": "string",
            "maxLength": 25000,
            "description": "SVG path data string"
          },
          "strokeColor": {
            "type": "string",
            "description": "Stroke color as hex code",
            "default": "#000000"
          },
          "strokeWidth": {
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "description": "Stroke width in pixels",
            "default": 2
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          }
        },
        "required": [
          "type",
          "pathData"
        ],
        "additionalProperties": false,
        "title": "NestedPathAnnotationInput",
        "description": "Nested path annotation"
      },
      "NestedAnnotationInputList": {
        "type": "array",
        "items": {
          "oneOf": [
            {
              "$ref": "#/components/schemas/NestedDotAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/NestedShapeAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/NestedTextAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/NestedPathAnnotationInput"
            }
          ],
          "title": "NestedAnnotationInput"
        },
        "maxItems": 10,
        "description": "Optional array of nested annotations (spatial annotations within this annotation's context). IMPORTANT - Nested annotations can ONLY be used when the parent annotation has a timestamp (timestamp, startTimestamp, or endTimestamp). Nested annotations inherit the parent's timestamp and cannot have their own timestamps or nested annotations."
      },
      "DotAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "dot"
            ]
          },
          "coordinates": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "X coordinate as percentage (0-100)"
              },
              "y": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "Y coordinate as percentage (0-100)"
              }
            },
            "required": [
              "x",
              "y"
            ],
            "additionalProperties": false,
            "title": "AnnotationCoordinatesInput"
          },
          "radius": {
            "type": "number",
            "minimum": 5,
            "maximum": 100,
            "default": 10
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "timestamp": {
            "type": "number",
            "description": "Legacy single timestamp in milliseconds (for backward compatibility)"
          },
          "startTimestamp": {
            "type": "number",
            "description": "Range start timestamp or single point timestamp in milliseconds"
          },
          "endTimestamp": {
            "type": "number",
            "description": "Range end timestamp in milliseconds (optional, creates range if provided)"
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          },
          "nestedAnnotations": {
            "$ref": "#/components/schemas/NestedAnnotationInputList"
          }
        },
        "required": [
          "type",
          "coordinates"
        ],
        "additionalProperties": false,
        "title": "DotAnnotationInput"
      },
      "FrameCommentAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "frameComment"
            ]
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "timestamp": {
            "type": "number",
            "description": "Legacy single timestamp in milliseconds (for backward compatibility)"
          },
          "startTimestamp": {
            "type": "number",
            "description": "Range start timestamp or single point timestamp in milliseconds"
          },
          "endTimestamp": {
            "type": "number",
            "description": "Range end timestamp in milliseconds (optional, creates range if provided)"
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          },
          "nestedAnnotations": {
            "$ref": "#/components/schemas/NestedAnnotationInputList"
          }
        },
        "required": [
          "type"
        ],
        "additionalProperties": false,
        "title": "FrameCommentAnnotationInput"
      },
      "ShapeAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "rectangle",
              "circle",
              "triangle",
              "arrow",
              "line"
            ]
          },
          "coordinates": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "x": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "X coordinate as percentage (0-100)"
                },
                "y": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Y coordinate as percentage (0-100)"
                }
              },
              "required": [
                "x",
                "y"
              ],
              "additionalProperties": false,
              "title": "AnnotationCoordinatesInput"
            },
            "minItems": 2,
            "maxItems": 50,
            "description": "Array of coordinate points defining the shape"
          },
          "strokeColor": {
            "type": "string",
            "description": "Stroke color as hex code",
            "default": "#000000"
          },
          "fillColor": {
            "type": "string",
            "description": "Fill color as hex code (optional)",
            "default": null
          },
          "strokeWidth": {
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "description": "Stroke width in pixels",
            "default": 2
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "timestamp": {
            "type": "number",
            "description": "Legacy single timestamp in milliseconds (for backward compatibility)"
          },
          "startTimestamp": {
            "type": "number",
            "description": "Range start timestamp or single point timestamp in milliseconds"
          },
          "endTimestamp": {
            "type": "number",
            "description": "Range end timestamp in milliseconds (optional, creates range if provided)"
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          },
          "nestedAnnotations": {
            "$ref": "#/components/schemas/NestedAnnotationInputList"
          }
        },
        "required": [
          "type",
          "coordinates"
        ],
        "additionalProperties": false,
        "title": "ShapeAnnotationInput"
      },
      "TextAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "text"
            ]
          },
          "coordinates": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "X coordinate as percentage (0-100)"
              },
              "y": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "Y coordinate as percentage (0-100)"
              }
            },
            "required": [
              "x",
              "y"
            ],
            "additionalProperties": false,
            "title": "AnnotationCoordinatesInput"
          },
          "content": {
            "type": "string",
            "maxLength": 500,
            "description": "Text content to display"
          },
          "fontSize": {
            "type": "number",
            "minimum": 8,
            "maximum": 72,
            "description": "Font size in pixels",
            "default": 16
          },
          "fontFamily": {
            "type": "string",
            "default": "Arial",
            "enum": [
              "Arial",
              "Helvetica",
              "Times New Roman",
              "Courier New",
              "Georgia",
              "Verdana"
            ]
          },
          "fontWeight": {
            "type": "string",
            "default": "normal",
            "enum": [
              "normal",
              "bold"
            ]
          },
          "fontStyle": {
            "type": "string",
            "default": "normal",
            "enum": [
              "normal",
              "italic"
            ]
          },
          "textColor": {
            "type": "string",
            "description": "Text color as hex code",
            "default": "#000000"
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "timestamp": {
            "type": "number",
            "description": "Legacy single timestamp in milliseconds (for backward compatibility)"
          },
          "startTimestamp": {
            "type": "number",
            "description": "Range start timestamp or single point timestamp in milliseconds"
          },
          "endTimestamp": {
            "type": "number",
            "description": "Range end timestamp in milliseconds (optional, creates range if provided)"
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          },
          "nestedAnnotations": {
            "$ref": "#/components/schemas/NestedAnnotationInputList"
          }
        },
        "required": [
          "type",
          "coordinates",
          "content"
        ],
        "additionalProperties": false,
        "title": "TextAnnotationInput"
      },
      "PathAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "path"
            ]
          },
          "pathData": {
            "type": "string",
            "maxLength": 25000,
            "description": "SVG path data string"
          },
          "strokeColor": {
            "type": "string",
            "description": "Stroke color as hex code",
            "default": "#000000"
          },
          "strokeWidth": {
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "description": "Stroke width in pixels",
            "default": 2
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "frame": {
            "type": "number",
            "description": "Video frame number (for video annotations)",
            "default": 0
          },
          "timestamp": {
            "type": "number",
            "description": "Legacy single timestamp in milliseconds (for backward compatibility)"
          },
          "startTimestamp": {
            "type": "number",
            "description": "Range start timestamp or single point timestamp in milliseconds"
          },
          "endTimestamp": {
            "type": "number",
            "description": "Range end timestamp in milliseconds (optional, creates range if provided)"
          },
          "left": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "X position as percentage of media width"
          },
          "top": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Y position as percentage of media height"
          },
          "width": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Width as percentage of media width"
          },
          "height": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 100,
            "description": "Height as percentage of media height"
          },
          "scaleX": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Horizontal scaling factor",
            "default": 1
          },
          "scaleY": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 10,
            "description": "Vertical scaling factor",
            "default": 1
          },
          "angle": {
            "type": "number",
            "minimum": -360,
            "maximum": 360,
            "description": "Rotation angle in degrees",
            "default": 0
          },
          "skewX": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "X-axis skewing in degrees",
            "default": 0
          },
          "skewY": {
            "type": "number",
            "minimum": -89,
            "maximum": 89,
            "description": "Y-axis skewing in degrees",
            "default": 0
          },
          "flipX": {
            "type": "boolean",
            "description": "Horizontal flip",
            "default": false
          },
          "flipY": {
            "type": "boolean",
            "description": "Vertical flip",
            "default": false
          },
          "originX": {
            "type": "string",
            "description": "Transform origin X",
            "default": "left",
            "enum": [
              "left",
              "center",
              "right"
            ]
          },
          "originY": {
            "type": "string",
            "description": "Transform origin Y",
            "default": "top",
            "enum": [
              "center",
              "top",
              "bottom"
            ]
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Object opacity",
            "default": 1
          },
          "visible": {
            "type": "boolean",
            "description": "Object visibility",
            "default": true
          },
          "shadow": {
            "$ref": "#/components/schemas/SharedColorBlurOffsetX"
          },
          "strokeLineCap": {
            "type": "string",
            "description": "Line cap style",
            "default": "butt",
            "enum": [
              "butt",
              "round",
              "square"
            ]
          },
          "strokeLineJoin": {
            "type": "string",
            "description": "Line join style",
            "default": "miter",
            "enum": [
              "miter",
              "round",
              "bevel"
            ]
          },
          "strokeMiterLimit": {
            "type": "number",
            "minimum": 1,
            "maximum": 20,
            "description": "Miter limit for line joins",
            "default": 4
          },
          "strokeDashArray": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 10
            },
            "maxItems": 10,
            "description": "Dash pattern as multiples of stroke width"
          },
          "fillRule": {
            "type": "string",
            "description": "Fill rule for complex shapes",
            "default": "nonzero",
            "enum": [
              "nonzero",
              "evenodd"
            ]
          },
          "nestedAnnotations": {
            "$ref": "#/components/schemas/NestedAnnotationInputList"
          }
        },
        "required": [
          "type",
          "pathData"
        ],
        "additionalProperties": false,
        "title": "PathAnnotationInput"
      },
      "Dot3dAnnotationInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "dot3d"
            ]
          },
          "position": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number",
                "minimum": -10000,
                "maximum": 10000,
                "description": "X coordinate as percentage (0-100)"
              },
              "y": {
                "type": "number",
                "minimum": -10000,
                "maximum": 10000,
                "description": "Y coordinate as percentage (0-100)"
              },
              "z": {
                "type": "number",
                "minimum": -10000,
                "maximum": 10000
              }
            },
            "required": [
              "x",
              "y",
              "z"
            ],
            "additionalProperties": false
          },
          "normal": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number",
                "minimum": -1,
                "maximum": 1,
                "description": "X coordinate as percentage (0-100)"
              },
              "y": {
                "type": "number",
                "minimum": -1,
                "maximum": 1,
                "description": "Y coordinate as percentage (0-100)"
              },
              "z": {
                "type": "number",
                "minimum": -1,
                "maximum": 1
              }
            },
            "required": [
              "x",
              "y",
              "z"
            ],
            "additionalProperties": false
          },
          "scale": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 10,
            "default": 1
          },
          "color": {
            "type": "string",
            "description": "Hex color code (e.g.,",
            "default": "#9b9b9b"
          },
          "nestedAnnotations": {
            "$ref": "#/components/schemas/NestedAnnotationInputList"
          }
        },
        "required": [
          "type",
          "position",
          "normal"
        ],
        "additionalProperties": false,
        "title": "Dot3dAnnotationInput"
      },
      "AnnotationInputList": {
        "type": "array",
        "items": {
          "oneOf": [
            {
              "$ref": "#/components/schemas/DotAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/FrameCommentAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/ShapeAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/TextAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/PathAnnotationInput"
            },
            {
              "$ref": "#/components/schemas/Dot3dAnnotationInput"
            }
          ],
          "title": "AnnotationInput"
        },
        "maxItems": 100,
        "description": "Annotations attached to this message. `attachments`, `mentions`, `assetMentions` and `quotes` are rejected in public chats."
      },
      "SharedContentReplyToIdAnnotations": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000,
            "description": "The message content (HTML-sanitized server-side)"
          },
          "replyToId": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the message being replied to"
          },
          "annotations": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/DotAnnotationInput"
                },
                {
                  "$ref": "#/components/schemas/FrameCommentAnnotationInput"
                },
                {
                  "$ref": "#/components/schemas/ShapeAnnotationInput"
                },
                {
                  "$ref": "#/components/schemas/TextAnnotationInput"
                },
                {
                  "$ref": "#/components/schemas/PathAnnotationInput"
                },
                {
                  "$ref": "#/components/schemas/Dot3dAnnotationInput"
                }
              ],
              "title": "AnnotationInput"
            },
            "maxItems": 100,
            "description": "Array of annotations for images/videos"
          },
          "guestId": {
            "type": "string",
            "format": "uuid",
            "description": "Client-generated identity for an anonymous commenter, persisted\nin the visitor's localStorage per token. Keeps two same-named\nguests distinct. Ignored when authenticated.\n"
          },
          "guestName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40,
            "description": "Display name for an anonymous commenter (HTML-sanitized). Required\n(and only used) when posting without authentication on an opted-in\nrelease.\n"
          },
          "guestColor": {
            "type": "string",
            "enum": [
              "#37474F",
              "#FF5722",
              "#2962FF",
              "#33691E",
              "#00796B",
              "#455A64",
              "#2979FF",
              "#827717",
              "#7986CB",
              "#8E24AA",
              "#9575CD",
              "#BF360C",
              "#01579B",
              "#EF6C00",
              "#AA00FF",
              "#F44336",
              "#7C4DFF",
              "#E65100",
              "#8D6E63",
              "#283593",
              "#607D8B",
              "#009688",
              "#FF5252",
              "#03A9F4",
              "#C2185B",
              "#00ACC1",
              "#E91E63",
              "#5D4037",
              "#78909C",
              "#1E88E5",
              "#D500F9",
              "#7E57C2",
              "#5C6BC0",
              "#558B2F",
              "#2E7D32",
              "#F50057",
              "#004D40",
              "#0D47A1",
              "#C51162",
              "#D50000",
              "#6200EA",
              "#00BCD4",
              "#0277BD"
            ],
            "description": "Color the anonymous commenter selected — the visible\ndifferentiator between guests. Must be one of the platform's\napproved colors (`colors.approvedColors` from `GET /config`).\nIgnored when authenticated.\n",
            "example": "#2962FF"
          }
        },
        "required": [
          "content"
        ],
        "additionalProperties": false
      }
    },
    "responses": {
      "UnknownError": {
        "description": "An unknown error has occurred.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "unknownError",
              "code": 500,
              "message": "An unknown error has occurred."
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Authentication is required but was missing or failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "unauthorized",
              "code": 401,
              "message": "Please authenticate"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The authenticated user does not have permission to perform this action.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "forbidden",
              "code": 403,
              "message": "Forbidden"
            }
          }
        }
      },
      "CapabilityNotAvailable": {
        "description": "Returned by `requireCapability(...)`-gated routes when the\nworkspace's plan does not include the capability or the\nper-capability opt-in setting (`aiAddOnEnabled`, `boardsEnabled`,\n`convosEnabled`) is off. Distinct from `Forbidden` because the\ncaller's user-level permission is fine — what's missing is the\nworkspace-level entitlement.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "capabilityNotAvailable",
              "code": 403,
              "message": "This feature is not available on the workspace’s current plan or has not been enabled."
            }
          }
        }
      },
      "NotFound": {
        "description": "The requested resource could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "notFound",
              "code": 404,
              "message": "Not found"
            }
          }
        }
      },
      "UserNotFound": {
        "description": "The specified user could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "userNotFound",
              "code": 404,
              "message": "User not found."
            }
          }
        }
      },
      "WorkspaceNotFound": {
        "description": "The specified workspace could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "workspaceNotFound",
              "code": 404,
              "message": "Workspace not found."
            }
          }
        }
      },
      "ProjectNotFound": {
        "description": "The specified project could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "projectNotFound",
              "code": 404,
              "message": "Project not found."
            }
          }
        }
      },
      "InviteNotFound": {
        "description": "The specified invitation could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "inviteNotFound",
              "code": 404,
              "message": "Invite not found."
            }
          }
        }
      },
      "AssetNotFound": {
        "description": "The specified asset could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "assetNotFound",
              "code": 400,
              "message": "Asset not found."
            }
          }
        }
      },
      "FileNotFound": {
        "description": "The specified file associated with an asset could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "fileNotFound",
              "code": 400,
              "message": "File not found."
            }
          }
        }
      },
      "ChatNotFound": {
        "description": "The specified chat could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "chatNotFound",
              "code": 404,
              "message": "Chat does not exist."
            }
          }
        }
      },
      "BadRequest": {
        "description": "The request could not be understood or was missing required parameters.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "badRequest",
              "code": 400,
              "message": "Bad request."
            }
          }
        }
      },
      "TaskNotFound": {
        "description": "The specified task could not be found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "taskNotFound",
              "code": 404,
              "message": "Task not found."
            }
          }
        }
      },
      "SubmissionNotFound": {
        "description": "The specified submission could not be found in this project.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "submissionNotFound",
              "code": 404,
              "message": "Submission not found."
            }
          }
        }
      },
      "PublicFileSystemNotFound": {
        "description": "The specified public file system could not be found in this project.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "type": "publicFileSystemNotFound",
              "code": 404,
              "message": "Public file system not found"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "tags": [
    {
      "name": "AccessActivity",
      "description": "Play, download and embed metrics for assets. `download` and\n`embed_resolved` events are recorded automatically by the API whenever\na signed download URL is issued, embed files are resolved, or a file is\ndownloaded from the Nurama web app. `play_started` and `play_completed`\nevents are reported by the player through the POST endpoints below.\nReads return aggregated rollups suitable for dashboards.\n\nCounts are aggregated per day for each combination of `(assetId,\neventType, visibility, referrerHost, country, userAgentClass)`, so all\nplays of one asset on one day from one host contribute to a single\nbucket. The `visibility` dimension is the audience (`creator` |\n`reviewer` | `public`), matching the visibility model used elsewhere.\n"
    },
    {
      "name": "AI",
      "description": "AI features exposed under `/v1/ai/*`. Every AI call is metered against the\ncalling workspace's Nurama Credit balance, except Nurama Support chat,\nwhich is free.\n\nSurfaces:\n\n  - **Polish** (`POST /ai/polish`) — rewrite a draft message in a chosen tone.\n  - **Compose** (`POST /ai/compose`) — multi-turn \"Compose with Nu\" drafting\n    conversation for a chat; returns commentary plus an optional proposed\n    message.\n  - **Task generation** (`POST /ai/task-generation`) — turn a chat message\n    (plus surrounding context) into one or more board-task drafts. Requires\n    both the AI and the Boards capabilities on the workspace.\n  - **AI chat** (`/ai/chat/*`) — multi-turn private conversation with the\n    Nurama assistant, scoped to a workspace, a project, or a personal\n    (social) chat. The assistant can call tools on the caller's behalf.\n  - **Image revision** (`/ai/revisions*`) — prompt-guided image edits, staged\n    as temporary Scratch uploads until promoted to an asset.\n  - **Support chat** (`/ai/support-chat/*`) — free Nurama Support assistant,\n    outside any workspace.\n  - **Balance / usage** (`/ai/balance`, `/ai/usage`) — also available at\n    `/credits/balance` and `/credits/usage`.\n\n**Credits.** A workspace's balance is the sum of its monthly plan grant\n(`planBalance`, which does not carry over) and any purchased top-ups\n(`purchasedBalance`, which accumulate). Each billable call is charged its\nactual cost once it completes; the response reports `billedCredits` and\n`balanceAfter`. If the AI provider call fails, nothing is charged.\n\n**What every billable AI endpoint checks**, in order. Each check has its\nown error `type`:\n\n  1. The caller holds the endpoint's permission on the workspace\n     (`forbidden`, 403).\n  2. The workspace has an active subscription (`subscriptionNotActive`, 400).\n  3. The workspace's plan includes the `ai` capability and the AI add-on is\n     switched on for the workspace (`capabilityNotAvailable`, 403, with\n     `errorData: { capability, workspaceId }`).\n  4. The feature is enabled in the effective AI settings — a project's\n     settings override the workspace's, key by key. `allowAiFeatures` and\n     the per-feature flag (`aiPolishEnabled`, `aiComposeEnabled`,\n     `aiChatEnabled`, `aiTaskGenerationEnabled`, `aiImageRevisionEnabled`)\n     must both be on, and reviewers additionally need the matching\n     `*AllowReviewer` flag (`aiFeatureNotEnabled`, 403).\n  5. The workspace balance covers the worst-case cost of the call and the\n     caller is under their per-user daily AI spend budget\n     (`creditBalanceInsufficient` or `creditOrderExceedsUserDailySpendLimit`,\n     400, with `errorData` describing the shortfall).\n\n**Live updates.** AI chat activity is delivered on the caller's\n`/user/{userId}` WebSocket channel as `notification` events whose `type`\nis `aiChatTopicCreate`, `aiChatTopicUpdate` or `aiChatMessageCreate`.\nAssistant replies carry the Nurama AI system user as the initiator;\nmessages the user sends carry their own id.\n"
    },
    {
      "name": "Assets",
      "description": "Manage assets and files."
    },
    {
      "name": "Boards",
      "description": "Kanban boards for organizing project tasks into columns."
    },
    {
      "name": "Board Columns",
      "description": "Manage columns within a kanban board."
    },
    {
      "name": "Board Tasks",
      "description": "Create, move, and manage tasks on a kanban board."
    },
    {
      "name": "Task Details",
      "description": "Update task details (subject, description, assignee) outside of board context."
    },
    {
      "name": "Task Links",
      "description": "Link and unlink tasks with relationship types (related, blocks, blockedBy, duplicate)."
    },
    {
      "name": "Board Following",
      "description": "Follow and unfollow boards to receive notifications about new tasks."
    },
    {
      "name": "Bots",
      "description": "Bot user management for programmatic API access. A bot is a workspace-scoped user (accountType 'bot') that authenticates with an API key instead of a JWT. Limited to 2 free bots per workspace. Requires the canManageBots permission."
    },
    {
      "name": "Chats",
      "description": "Chat and ChatMessage routes."
    },
    {
      "name": "Config",
      "description": "Public platform configuration that clients read at startup — validation limits, roles and permissions, supported file types and feature flags."
    },
    {
      "name": "Convos",
      "description": "Video and audio conversation (convo) routes for real-time communication using Daily.co integration. Every `/convos` route is behind the server-side convos feature flag and responds with 403 `featureDisabled` when it is off.\n"
    },
    {
      "name": "Folders",
      "description": "Folder management and retrieval"
    },
    {
      "name": "Invites",
      "description": "API for invite management. Note - InviteeId and Invitee object will only be added to response if the invite has already been accepted."
    },
    {
      "name": "Link Preview",
      "description": "Fetch rich link preview metadata (Open Graph / meta tags) for URLs. Previews are signed with HMAC-SHA256 to prevent spoofing when submitted with chat messages."
    },
    {
      "name": "Membership",
      "description": "Membership management and retrieval. New membership can only be created through an invite. Once a record exists new roles may be added."
    },
    {
      "name": "Notifications",
      "description": "Retrieve notifications and notification last seen records.\n\nChannels are strings of the form `[resourceType]/[resourceId]` or\n`[resourceType]/[resourceId]/[visibility]` (a leading `/` is tolerated).\n`types` values are notification type names — the same `type` values\ncarried by WebSocket `notification` events (for example `chatMessageCreate`\nor `taskCreate`); every type is documented in the WebSocket event\nreference. `POST /notifications`, `/new`, `/count` and `/count/bulk`\nrequire the `canGetNotificationChannels` permission: the caller must be\nallowed to read every channel named in the body, otherwise `403`.\n"
    },
    {
      "name": "Projects",
      "description": "Project management and retrieval"
    },
    {
      "name": "Public",
      "description": "Public file system access (no authentication required)"
    },
    {
      "name": "PublicAssetLinks",
      "description": "Manage temporary, publicly shareable download links for individual assets."
    },
    {
      "name": "Scratch",
      "description": "Scratch objects are temporary uploads that live outside a project's\nasset tree until they are either promoted to a real Asset or expire.\nEach scratch object has an explicit lifecycle:\n`pendingUpload` → `active` → `promoted` or `expired`.\n\n**The upload flow mirrors assets.** The operation that starts a job\n(for example `POST /v1/ai/revisions`) returns a\n`{ scratchId, uploadId, key, urls[] }` bundle in the same shape\n`POST /v1/assets` returns for asset uploads. Upload each part\ndirectly to its signed URL, then call\n`POST /v1/scratch/{scratchId}/complete-upload` with the collected\n`{ uploadId, parts }` — the same body `POST /v1/assets/complete-upload`\naccepts.\n\n**There is no endpoint for creating scratch objects or requesting\nupload URLs directly.** Upload URLs are issued by the operation that\nowns the job, and discarding a scratch object is also handled by that\noperation so the audit and billing trail stays tied to a real job.\nPromotion to a real Asset is exposed via\n`POST /v1/scratch/{scratchId}/promote`.\n\nScratch bytes count toward the workspace's storage quota. When a\nscratch object expires or is discarded its bytes are released; when\nit is promoted the bytes are accounted for on the resulting Asset\ninstead, so nothing is counted twice.\n"
    },
    {
      "name": "Settings",
      "description": "Resource-specific user settings management"
    },
    {
      "name": "ShortLinks",
      "description": "Short link generation and resolution for shareable deep links."
    },
    {
      "name": "Storage",
      "description": "Retrieve resource storage data."
    },
    {
      "name": "Task Relations",
      "description": "\"Related To\" links between board tasks and chats / chat messages. Every route requires the workspace `boards` capability (403 `capabilityNotAvailable` otherwise) and an active workspace subscription.\n"
    },
    {
      "name": "Tasks",
      "description": "Retrieve and update tasks assigned to logged in user."
    },
    {
      "name": "Users",
      "description": "User management and retrieval\n\nDelegated credentials (personal access tokens, bot keys and OAuth access tokens) are rejected on PATCH /users and DELETE /users (GET /users stays available to delegated credentials) with 403 `delegatedTokenForbidden`; a first-party user session is required.\n"
    },
    {
      "name": "Version",
      "description": "Version and other deployment information."
    },
    {
      "name": "Webhook Subscriptions",
      "description": "Outbound webhook subscriptions. A workspace admin registers an HTTPS URL plus the event names it wants; Nurama POSTs a signed JSON envelope to that URL whenever a matching notification is written. All endpoints require `canManageWebhooks` on the workspace and either a Nurama app session or a personal access token carrying the `webhooks:manage` scope. Bot API keys and MCP connections cannot manage subscriptions. A workspace may hold at most 25 subscriptions that are not `failedOut`.\n\n**Signing.** Every subscription has an HMAC signing secret. The plaintext is returned exactly twice in its lifetime — on create and on rotate-secret — and is otherwise unrecoverable. Every other response is sanitised (the encrypted secret and its key version are never returned).\n\n**Event catalogue:** `task.created`, `task.updated` (covers move / assign / subject / description edits), `task.deleted`, `chat.message.created`, `asset.published` (only the transition to `active`), and `webhook.test` (synthetic; only fired by the `/test` endpoint and delivered to the targeted subscription regardless of its `events` list). Event names are not versioned; the wire contract is additive-only.\n\n**Lifecycle.** `active` → delivering; `paused` → manually paused by an admin; `failedOut` → automatically paused after 50 consecutive deliveries exhausted their retries (`failedOutAt` / `failedOutReason` set). Admins re-enable by PATCHing `status: active`, which clears the failed-out audit fields. An optional `expiresAt` stops fan-out once in the past."
    },
    {
      "name": "Workspaces",
      "description": "Workspaces management and retrieval"
    }
  ]
}