{
  "openapi": "3.0.1",
  "info": {
    "title": "3D Print Log Api",
    "description": "HTTP API powering <https://www.3dprintlog.com>, allowing users to manage their prints, printers, and filaments.\r\n\r\nAuthentication, scopes, errors, rate limits and which endpoints are stable are documented at <https://www.3dprintlog.com/docs/api>, and summarized for agents at <https://www.3dprintlog.com/auth.md>. For the rest of the product, see <https://www.3dprintlog.com/docs/getting-started>. Please contact us at <hello@3dprintlog.com> with any questions or comments.\r\n\r\n## Authentication\r\n\r\nRequests authenticate as a user, either with a personal API key or with an OAuth 2.0 access token.\r\n\r\n### Personal API key\r\n\r\nAfter creating an account on the 3D Print Log website, create an API key using the following:\r\n- Navigate to the [Personal Api Keys](https://www.3dprintlog.com/api-keys) page by clicking on your User Profile Picture at the top-left, and selecting \"Personal Api Keys\".\r\n- Click Create new API Key.\r\n- Enter a new description (such as \"API Access Key\").\r\n- Click Submit to generate a new key.\r\n- Copy the new 32-character key.\r\n   - Note: The API Key cannot be retrieved after you leave the page, so copy it to a secure location, otherwise you will have to generate a new key\r\n\r\nThe API key can be used either by adding a **X-Api-Key header** with the key, or by including a **api_key query param** to each request. A few account-management endpoints (registering push devices, reading the account's email address) accept only an OAuth token, and say so in their security requirements.\r\n\r\n### OAuth 2.0\r\n\r\nThe authorization code flow with PKCE, against the authorization server in the `oauth2` security scheme. Send the access token as `Authorization: Bearer <token>`.\r\n\r\n### AI agents\r\n\r\nAgents should prefer the MCP server at <https://api.3dprintlog.com/mcp>, which is built for tool calling and advertises its own OAuth metadata (RFC 9728). See <https://www.3dprintlog.com/docs/mcp>.\r\n",
    "contact": {
      "name": "Christopher Hoffman",
      "url": "https://www.hoffman.engineering",
      "email": "hello@3dprintlog.com"
    },
    "license": {
      "name": "AGPL-3.0",
      "url": "https://github.com/HoffmanEngineering/3d-print-log-api/blob/main/LICENSE"
    },
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.3dprintlog.com"
    }
  ],
  "paths": {
    "/api/achievements/catalog": {
      "get": {
        "tags": [
          "Achievements"
        ],
        "summary": "Get the achievement catalog. Hidden badges are counted but never described.",
        "operationId": "Achievements_GetCatalog",
        "responses": {
          "200": {
            "description": "The catalog, with each tier's rarity.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementCatalogDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementCatalogDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementCatalogDto"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/achievements/me": {
      "get": {
        "tags": [
          "Achievements"
        ],
        "summary": "Get the current user's achievements, progress and next hint. Grants anything the user\r\nqualifies for but does not hold yet, first.",
        "operationId": "Achievements_GetMine",
        "responses": {
          "200": {
            "description": "The user's achievements.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.MyAchievementsDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.MyAchievementsDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.MyAchievementsDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/users/{id}/achievements": {
      "get": {
        "tags": [
          "Achievements"
        ],
        "summary": "Get another maker's earned achievements for their public profile. Follows the profile's\r\nvisibility and the owner's \"show on profile\" setting; anything the viewer may not see comes\r\nback empty rather than as an error, so a logged-out visitor is never bounced.",
        "operationId": "Achievements_GetForUser",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The maker's user id.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The earned achievements, or an empty result.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.PublicAchievementsDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.PublicAchievementsDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.PublicAchievementsDto"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/achievements/hint/dismiss": {
      "post": {
        "tags": [
          "Achievements"
        ],
        "summary": "Dismiss the hint card for one badge tier. It stays hidden until a different hint is chosen.",
        "operationId": "Achievements_DismissHint",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.DismissHintRequestDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.DismissHintRequestDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.DismissHintRequestDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Dismissed."
          },
          "400": {
            "description": "The key or tier is not in the catalog.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Analytics/overview": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get headline totals (print count, success rate, filament, print time and cost) and prints by status over time.",
        "description": "Covers the authenticated user's own prints only. Every analytics endpoint takes the same\r\nfilter: a half-open `fromDate`/`toDate` range (omit both for all time), an IANA\r\n`timeZone` for bucketing (default UTC), optional `printerIds`,\r\n`filamentIds`, `projectIds` and `statuses`, a `granularity`\r\n(Auto, Day, Week or Month) and `comparePrevious` to add the preceding window of the\r\nsame length. Ids the caller does not own match nothing.",
        "operationId": "Analytics_GetOverview",
        "parameters": [
          {
            "name": "FromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ToDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "TimeZone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "PrinterIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "FilamentIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "ProjectIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "Statuses",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
              }
            }
          },
          {
            "name": "Granularity",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AnalyticsGranularity"
            }
          },
          {
            "name": "ComparePrevious",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "HasRange",
            "in": "query",
            "description": "True when both ends of the range are set. The attribute is what lets callers write\r\n`filter.FromDate.Value` after gating on this — without it the compiler cannot see\r\nthat a property getter implies anything about two other properties, and every such\r\ndereference raises CS8629.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The overview figures for the filtered window.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.OverviewResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.OverviewResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.OverviewResponse"
                }
              }
            }
          },
          "400": {
            "description": "The filter is invalid, for example a one-sided or reversed date range.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Analytics/activity": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get print activity over time: print count, print time, material and cost per period, a calendar of print days, streaks, and when prints start.",
        "description": "Covers the authenticated user's own prints only. Every analytics endpoint takes the same\r\nfilter: a half-open `fromDate`/`toDate` range (omit both for all time), an IANA\r\n`timeZone` for bucketing (default UTC), optional `printerIds`,\r\n`filamentIds`, `projectIds` and `statuses`, a `granularity`\r\n(Auto, Day, Week or Month) and `comparePrevious` to add the preceding window of the\r\nsame length. Ids the caller does not own match nothing.",
        "operationId": "Analytics_GetActivity",
        "parameters": [
          {
            "name": "FromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ToDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "TimeZone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "PrinterIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "FilamentIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "ProjectIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "Statuses",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
              }
            }
          },
          {
            "name": "Granularity",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AnalyticsGranularity"
            }
          },
          {
            "name": "ComparePrevious",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "HasRange",
            "in": "query",
            "description": "True when both ends of the range are set. The attribute is what lets callers write\r\n`filter.FromDate.Value` after gating on this — without it the compiler cannot see\r\nthat a property getter implies anything about two other properties, and every such\r\ndereference raises CS8629.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The activity series and calendar for the filtered window.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.ActivityResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.ActivityResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.ActivityResponse"
                }
              }
            }
          },
          "400": {
            "description": "The filter is invalid, for example a one-sided or reversed date range.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Analytics/printers": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get per-printer usage, success rate and maintenance for the filtered window, including idle printers.",
        "description": "Covers the authenticated user's own prints only. Every analytics endpoint takes the same\r\nfilter: a half-open `fromDate`/`toDate` range (omit both for all time), an IANA\r\n`timeZone` for bucketing (default UTC), optional `printerIds`,\r\n`filamentIds`, `projectIds` and `statuses`, a `granularity`\r\n(Auto, Day, Week or Month) and `comparePrevious` to add the preceding window of the\r\nsame length. Ids the caller does not own match nothing.",
        "operationId": "Analytics_GetPrinters",
        "parameters": [
          {
            "name": "FromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ToDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "TimeZone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "PrinterIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "FilamentIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "ProjectIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "Statuses",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
              }
            }
          },
          {
            "name": "Granularity",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AnalyticsGranularity"
            }
          },
          {
            "name": "ComparePrevious",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "HasRange",
            "in": "query",
            "description": "True when both ends of the range are set. The attribute is what lets callers write\r\n`filter.FromDate.Value` after gating on this — without it the compiler cannot see\r\nthat a property getter implies anything about two other properties, and every such\r\ndereference raises CS8629.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One row per printer, with a usage series and maintenance events.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.PrintersResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.PrintersResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.PrintersResponse"
                }
              }
            }
          },
          "400": {
            "description": "The filter is invalid, for example a one-sided or reversed date range.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Analytics/materials": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get material consumption by material type, brand and color, the most-used spools, how long each spool will last, and waste.",
        "description": "Covers the authenticated user's own prints only. Every analytics endpoint takes the same\r\nfilter: a half-open `fromDate`/`toDate` range (omit both for all time), an IANA\r\n`timeZone` for bucketing (default UTC), optional `printerIds`,\r\n`filamentIds`, `projectIds` and `statuses`, a `granularity`\r\n(Auto, Day, Week or Month) and `comparePrevious` to add the preceding window of the\r\nsame length. Ids the caller does not own match nothing.",
        "operationId": "Analytics_GetMaterials",
        "parameters": [
          {
            "name": "FromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ToDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "TimeZone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "PrinterIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "FilamentIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "ProjectIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "Statuses",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
              }
            }
          },
          {
            "name": "Granularity",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AnalyticsGranularity"
            }
          },
          {
            "name": "ComparePrevious",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "HasRange",
            "in": "query",
            "description": "True when both ends of the range are set. The attribute is what lets callers write\r\n`filter.FromDate.Value` after gating on this — without it the compiler cannot see\r\nthat a property getter implies anything about two other properties, and every such\r\ndereference raises CS8629.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Material usage for the filtered window.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaterialsResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaterialsResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaterialsResponse"
                }
              }
            }
          },
          "400": {
            "description": "The filter is invalid, for example a one-sided or reversed date range.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Analytics/costs": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get print spending split into filament, electricity and maintenance, grouped by material type and brand, with the cost of failed prints.",
        "description": "Covers the authenticated user's own prints only. Every analytics endpoint takes the same\r\nfilter: a half-open `fromDate`/`toDate` range (omit both for all time), an IANA\r\n`timeZone` for bucketing (default UTC), optional `printerIds`,\r\n`filamentIds`, `projectIds` and `statuses`, a `granularity`\r\n(Auto, Day, Week or Month) and `comparePrevious` to add the preceding window of the\r\nsame length. Ids the caller does not own match nothing.",
        "operationId": "Analytics_GetCosts",
        "parameters": [
          {
            "name": "FromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ToDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "TimeZone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "PrinterIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "FilamentIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "ProjectIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "Statuses",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
              }
            }
          },
          {
            "name": "Granularity",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AnalyticsGranularity"
            }
          },
          {
            "name": "ComparePrevious",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "HasRange",
            "in": "query",
            "description": "True when both ends of the range are set. The attribute is what lets callers write\r\n`filter.FromDate.Value` after gating on this — without it the compiler cannot see\r\nthat a property getter implies anything about two other properties, and every such\r\ndereference raises CS8629.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cost totals, groups and series for the filtered window.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CostsResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CostsResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CostsResponse"
                }
              }
            }
          },
          "400": {
            "description": "The filter is invalid, for example a one-sided or reversed date range.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Analytics/accuracy": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Compare slicer time and material estimates with what prints actually used, by printer, by material and over time.",
        "description": "Covers the authenticated user's own prints only. Every analytics endpoint takes the same\r\nfilter: a half-open `fromDate`/`toDate` range (omit both for all time), an IANA\r\n`timeZone` for bucketing (default UTC), optional `printerIds`,\r\n`filamentIds`, `projectIds` and `statuses`, a `granularity`\r\n(Auto, Day, Week or Month) and `comparePrevious` to add the preceding window of the\r\nsame length. Ids the caller does not own match nothing.",
        "operationId": "Analytics_GetAccuracy",
        "parameters": [
          {
            "name": "FromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ToDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "TimeZone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "PrinterIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "FilamentIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "ProjectIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "Statuses",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
              }
            }
          },
          {
            "name": "Granularity",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AnalyticsGranularity"
            }
          },
          {
            "name": "ComparePrevious",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "HasRange",
            "in": "query",
            "description": "True when both ends of the range are set. The attribute is what lets callers write\r\n`filter.FromDate.Value` after gating on this — without it the compiler cannot see\r\nthat a property getter implies anything about two other properties, and every such\r\ndereference raises CS8629.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Estimate accuracy overall, by printer and over time.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AccuracyResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AccuracyResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AccuracyResponse"
                }
              }
            }
          },
          "400": {
            "description": "The filter is invalid, for example a one-sided or reversed date range.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Comments/{id}": {
      "put": {
        "tags": [
          "Comments"
        ],
        "summary": "Edit a comment.",
        "operationId": "Comments_PutComment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Comment Id to edit",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "description": "The DTO containing the edited information.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.EditCommentDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.EditCommentDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.EditCommentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "An 201 Created if the edit was successful.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the user is not the owner of the comment.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Comments"
        ],
        "summary": "Delete a comment by comment id.",
        "operationId": "Comments_DeleteComment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The comment ID to delete",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An OK if the delete was successful."
          },
          "403": {
            "description": "Returned if the user is not the owner of the comment.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/connected-agents": {
      "get": {
        "tags": [
          "ConnectedAgents"
        ],
        "summary": "Gets the current user's connected AI agents.",
        "operationId": "ConnectedAgents_GetConnectedAgents",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.ConnectedAgentDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.ConnectedAgentDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.ConnectedAgentDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/connected-agents/{grantId}": {
      "delete": {
        "tags": [
          "ConnectedAgents"
        ],
        "summary": "Revokes one of the current user's connected AI agents by grant id.",
        "operationId": "ConnectedAgents_RevokeConnectedAgent",
        "parameters": [
          {
            "name": "grantId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Cura/settings": {
      "get": {
        "tags": [
          "Cura"
        ],
        "summary": "Returns the settings saved by cura by GUID. Since Cura does not know the user who first saved the settings,\r\nthe first time the settings are retrieved by a 3D Print Log user, the settings are linked to that user.\r\nOther users cannot retrieve the settings afterwards, even if they learned the GUID.",
        "operationId": "Cura_GetSettings",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The GUID for the settings",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An OK containing the settings send by Cura.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.CuraSetting"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.CuraSetting"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.CuraSetting"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the user is not the owner of the settings.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Cura"
        ],
        "summary": "Save a JSON object containing the settings from Cura.",
        "operationId": "Cura_SaveSettings",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.CuraSettings.NewCuraSettingsDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.CuraSettings.NewCuraSettingsDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.CuraSettings.NewCuraSettingsDto"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Devices": {
      "post": {
        "tags": [
          "Devices"
        ],
        "summary": "Registers or refreshes this installation's push token.",
        "operationId": "Devices_RegisterDevice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Device.RegisterDeviceDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Device.RegisterDeviceDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Device.RegisterDeviceDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The device was registered."
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          }
        ]
      }
    },
    "/api/Devices/{token}": {
      "delete": {
        "tags": [
          "Devices"
        ],
        "summary": "Removes this installation's push token.",
        "operationId": "Devices_DeleteDevice",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The device was removed, or was already absent."
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          }
        ]
      }
    },
    "/api/Feed": {
      "get": {
        "tags": [
          "Feed"
        ],
        "summary": "Get the ten most recent prints before a point in time, for a feed view. Not in general use.",
        "description": "Restricted to an allowlist of users; everyone else receives 404.",
        "operationId": "Feed_GetFeed",
        "parameters": [
          {
            "name": "fromDateTime",
            "in": "query",
            "description": "Return prints before this time. Defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintFeedSummaryDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintFeedSummaryDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintFeedSummaryDto"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Feedbacks": {
      "post": {
        "tags": [
          "Feedbacks"
        ],
        "summary": "Send a feedback.",
        "operationId": "Feedbacks_Post",
        "requestBody": {
          "description": "The feedback request.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Feedback.AddFeedbackDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Feedback.AddFeedbackDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Feedback.AddFeedbackDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Returned when feedback as been successfully sent."
          },
          "401": {
            "description": "Returned when the user is not authorized. Only logged-in users can send feedback."
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments": {
      "get": {
        "tags": [
          "Filaments"
        ],
        "summary": "List the current user's filaments, paged.",
        "description": "These are the user's filament spools, which the web app calls *materials*. Use a filament's\r\n`id` in a print's `filamentUsage` to record what the print consumed. Weights are in milligrams.",
        "operationId": "Filaments_GetFilamentSummariesForUser",
        "parameters": [
          {
            "name": "PageNumber",
            "in": "query",
            "description": "The 1-based page number for pagination.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PageSize",
            "in": "query",
            "description": "The number of items to return per page.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "SortDirection",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortDirection"
            }
          },
          {
            "name": "SortColumn",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortEnums.FilamentSummarySortColumn"
            }
          },
          {
            "name": "searchText",
            "in": "query",
            "description": "Search filament's name/description/brand for text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filterByMaterialCategoryNickname",
            "in": "query",
            "description": "Optional filter by a material category nickname",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filterByStorageLocation",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "includeInactive",
            "in": "query",
            "description": "Include filament rolls that have been marked as inactive.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "showFavoritesOnly",
            "in": "query",
            "description": "Show only the favoriate filaments",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "showLoadedFilamentOnly",
            "in": "query",
            "description": "Show only currently loaded filament",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "colorPatterns",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Enums.ColorPatternType"
              }
            }
          },
          {
            "name": "finishTypes",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentFinishType"
              }
            }
          },
          {
            "name": "effects",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentEffect"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the paged list of filament rolls.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Filament.FilamentSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Filament.FilamentSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Filament.FilamentSummaryDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Filaments"
        ],
        "summary": "Create a new Filament for the current user.",
        "description": "`source` says how the starting amount was measured: `1` Weight, `2` Length or `3` Volume. The\r\nother two are derived from it where the density and diameter allow. Weights are in milligrams.",
        "operationId": "Filaments_PostFilament",
        "requestBody": {
          "description": "The dto containing all of the details for the filament to create.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.AddFilamentDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.AddFilamentDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.AddFilamentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The filament detail DTO that was created.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the request is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/{id}": {
      "get": {
        "tags": [
          "Filaments"
        ],
        "summary": "Returns detailed information for a filament roll by ID.",
        "operationId": "Filaments_GetFilament",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The GUID of the filament.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The Filament details on success.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
                }
              }
            }
          },
          "404": {
            "description": "Returned when a filament with that GUID does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Returned when the requested filament does not belong to the current user."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Filaments"
        ],
        "summary": "Replace a filament's details.",
        "description": "Replaces the whole filament rather than patching it: read it with `GET /api/Filaments/{id}`,\r\nchange the fields you need, and send the whole object back.",
        "operationId": "Filaments_PutFilament",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The GUID of the filament to update.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "The updated filament details.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentDetailDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The updated filament information."
          },
          "400": {
            "description": "Returned if the id does not match the id of the filament details provided.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if a filament with the specified ID is not found.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the request is not authenticated."
          },
          "403": {
            "description": "Returned if the current user tries to update a filament which is not theirs."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Filaments"
        ],
        "summary": "Permantently delete a filament, if the filament has not been used in any existing prints.",
        "operationId": "Filaments_DeleteFilament",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the filament to delete.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Returned if the filament was deleted successfully."
          },
          "400": {
            "description": "Returned if the filament is unable to be deleted since it has been used in a print.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the current user cannot access the filament.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/storage-locations": {
      "get": {
        "tags": [
          "Filaments"
        ],
        "summary": "Returns a DTO which includes a list of all filament storage locations for the current user.",
        "operationId": "Filaments_GetFilamentStorageLocations",
        "responses": {
          "200": {
            "description": "The Filament Storage Locations on success.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentStorageLocationDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentStorageLocationDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentStorageLocationDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/purchase-locations": {
      "get": {
        "tags": [
          "Filaments"
        ],
        "summary": "Returns a DTO which includes a list of all filament purchase locations for the current user.",
        "operationId": "Filaments_GetFilamentPurchaseLocations",
        "responses": {
          "200": {
            "description": "The Filament Purchase Locations on success.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentPurchaseLocationsDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentPurchaseLocationsDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentPurchaseLocationsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/brands": {
      "get": {
        "tags": [
          "Filaments"
        ],
        "summary": "Returns a DTO which includes a list of all filament brands for the current user.",
        "operationId": "Filaments_GetFilamentBrands",
        "responses": {
          "200": {
            "description": "The Filament Brands on success.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentBrandsDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentBrandsDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentBrandsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/{id}/images": {
      "post": {
        "tags": [
          "Filaments"
        ],
        "summary": "Upload an image to a filament.",
        "description": "The uploaded bytes are decoded server-side; the client's declared content type is\r\nnot trusted. An undecodable or disallowed file is rejected before anything is stored.",
        "operationId": "Filaments_PostFilamentImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored image, with signed URLs.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentImageDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentImageDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentImageDto"
                }
              }
            }
          },
          "400": {
            "description": "The file is missing, too large, or not a supported image.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "No such filament belonging to the current user.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/{id}/images/{imageId}": {
      "get": {
        "tags": [
          "Filaments"
        ],
        "summary": "Redirects to a signed URL for a filament image.",
        "description": "For non-browser API consumers (MCP server, scripts) holding a bearer token.\r\nThe UI does not use this - it reads pre-signed URLs from the filament DTO.\r\nNOT usable from <img src>: the redirect itself requires the bearer token.",
        "operationId": "Filaments_GetFilamentImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Found"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Filaments"
        ],
        "summary": "Deletes a filament image, its file rows, and its blobs.",
        "operationId": "Filaments_DeleteFilamentImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/{id}/images/reorder": {
      "put": {
        "tags": [
          "Filaments"
        ],
        "summary": "Reorders a filament's images. The supplied IDs must be the filament's exact image set.",
        "operationId": "Filaments_ReorderFilamentImages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "text/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "application/*+json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Filaments/{id}/images/{imageId}/set-as-default": {
      "post": {
        "tags": [
          "Filaments"
        ],
        "summary": "Makes one of a filament's images its default.",
        "operationId": "Filaments_SetDefaultFilamentImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/MaterialCategories": {
      "get": {
        "tags": [
          "MaterialCategories"
        ],
        "summary": "Returns the current list of material types for the material categories dropdown.",
        "operationId": "MaterialCategories_GetMaterials",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.MaterialCategory.MaterialCategoryDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.MaterialCategory.MaterialCategoryDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.MaterialCategory.MaterialCategoryDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/MaterialTypes": {
      "get": {
        "tags": [
          "MaterialTypes"
        ],
        "summary": "Returns the current list of material types for the material selection dropdown.",
        "operationId": "MaterialTypes_GetMaterials",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Materials.MaterialTypeDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Materials.MaterialTypeDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Materials.MaterialTypeDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Moonraker/notifier": {
      "post": {
        "tags": [
          "Moonraker"
        ],
        "summary": "Receive a print event from Moonraker (Klipper).",
        "description": "Called by Moonraker's notifier on a Klipper printer, not by hand. Set it up as described at\r\nhttps://www.3dprintlog.com/docs/klipper, authenticating with a personal API key.\r\n            \r\nThe body is Moonraker's JSON notification, whose `message` field carries the print event.\r\n`started` creates a print in the Printing status. `complete` marks that print Success, and\r\n`cancelled` or `error` mark it Failed.",
        "operationId": "Moonraker_Webhook",
        "responses": {
          "200": {
            "description": "Returned if the webhook was handled successfully."
          },
          "400": {
            "description": "Returned if required data is missing in the webhook (like the printerId, etc).",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the current user cannot access the printer specified.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Notifications": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "Get a paged list of notifications for the current user.",
        "operationId": "Notifications_GetNotifications",
        "parameters": [
          {
            "name": "PageNumber",
            "in": "query",
            "description": "The 1-based page number for pagination.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PageSize",
            "in": "query",
            "description": "The number of items to return per page.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "unreadOnly",
            "in": "query",
            "description": "If true, only return unread notifications.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "If set, only return notifications of this type.",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.NotificationType"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paged list of notifications.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Notification.NotificationSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Notification.NotificationSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Notification.NotificationSummaryDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Notifications"
        ],
        "summary": "Delete all notifications for the current user.",
        "operationId": "Notifications_DeleteAllNotifications",
        "responses": {
          "204": {
            "description": "All notifications were deleted."
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Notifications/unread-count": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "Get the count of unread notifications for the current user.",
        "operationId": "Notifications_GetUnreadCount",
        "responses": {
          "200": {
            "description": "The unread notification count.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.NotificationUnreadCountDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.NotificationUnreadCountDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.NotificationUnreadCountDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Notifications/{id}": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "Get a single notification by ID.",
        "operationId": "Notifications_GetNotification",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The notification ID.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The notification details.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.NotificationDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.NotificationDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.NotificationDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if the notification is not found.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Notifications"
        ],
        "summary": "Delete a single notification.",
        "operationId": "Notifications_DeleteNotification",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The notification ID.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The notification was deleted."
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if the notification is not found.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Notifications/{id}/read": {
      "put": {
        "tags": [
          "Notifications"
        ],
        "summary": "Mark a single notification as read.",
        "operationId": "Notifications_MarkAsRead",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The notification ID.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The notification was marked as read."
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if the notification is not found.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Notifications/read-all": {
      "put": {
        "tags": [
          "Notifications"
        ],
        "summary": "Mark all notifications as read for the current user.",
        "operationId": "Notifications_MarkAllAsRead",
        "responses": {
          "204": {
            "description": "All notifications were marked as read."
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Notifications/read": {
      "put": {
        "tags": [
          "Notifications"
        ],
        "summary": "Mark multiple notifications as read.",
        "operationId": "Notifications_MarkMultipleAsRead",
        "requestBody": {
          "description": "The notification IDs to mark as read.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.MarkNotificationsReadDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.MarkNotificationsReadDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.MarkNotificationsReadDto"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The notifications were marked as read."
          },
          "400": {
            "description": "Returned if the request body is invalid.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Octoprint": {
      "post": {
        "tags": [
          "Octoprint"
        ],
        "summary": "Receive a print event from the OctoPrint Webhooks plugin.",
        "description": "Called by the OctoPrint-Webhooks plugin, not by hand. Set it up as described at\r\nhttps://www.3dprintlog.com/docs/octoprint-webhook, authenticating with a personal API key.\r\n            \r\nThe body is form data. `deviceIdentifier` must be the id of one of the caller's printers. A\r\n`Print Started` event creates a print in the Printing status. `Print Done` marks it Success,\r\nand `Print Failed` or `Error` mark it Failed.",
        "operationId": "Octoprint_Webhook",
        "parameters": [
          {
            "name": "Extra",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookExtraDto"
            }
          },
          {
            "name": "Job",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookJobDto"
            }
          },
          {
            "name": "Meta",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookMetaDto"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "DeviceIdentifier": {
                    "type": "string"
                  },
                  "ApiSecret": {
                    "type": "string"
                  },
                  "Topic": {
                    "type": "string"
                  },
                  "snapshot": {
                    "type": "string",
                    "format": "binary"
                  },
                  "CurrentTime": {
                    "type": "integer",
                    "description": "The Unix Epoch timestamp that it started.",
                    "format": "int64"
                  }
                }
              },
              "encoding": {
                "DeviceIdentifier": {
                  "style": "form"
                },
                "ApiSecret": {
                  "style": "form"
                },
                "Topic": {
                  "style": "form"
                },
                "snapshot": {
                  "style": "form"
                },
                "CurrentTime": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Returned if the webhook was handled successfully."
          },
          "400": {
            "description": "Returned if required data is missing in the webhook (like the DeviceIdentifier, etc).",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the current user cannot access the printer specified.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/PrinterCategories": {
      "get": {
        "tags": [
          "PrinterCategories"
        ],
        "summary": "Returns the current list of printer categories for the material selection dropdown.",
        "operationId": "PrinterCategories_GetMaterials",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterCategory.PrinterCategoryDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterCategory.PrinterCategoryDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterCategory.PrinterCategoryDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/PrinterMaintenance": {
      "get": {
        "tags": [
          "PrinterMaintenance"
        ],
        "summary": "Gets a Paged Result of printer maintenance entries",
        "operationId": "PrinterMaintenance_GetPrinterMaintenanceEntriesForCurrentUser",
        "parameters": [
          {
            "name": "PageNumber",
            "in": "query",
            "description": "The 1-based page number for pagination.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PageSize",
            "in": "query",
            "description": "The number of items to return per page.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "SortDirection",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortDirection"
            }
          },
          {
            "name": "SortColumn",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortEnums.PrinterMaintenanceSortColumn"
            }
          },
          {
            "name": "searchText",
            "in": "query",
            "description": "Search maintenance entries's category/description/notes for text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filterByPrinterIds",
            "in": "query",
            "description": "Filter by specific printer ids.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "includeDone",
            "in": "query",
            "description": "Whether to include entries marked as Done.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "includeNotDone",
            "in": "query",
            "description": "Whether to include entries marked as Done.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the paged list of printer maintenance entries.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "PrinterMaintenance"
        ],
        "summary": "Create a new Maintenance Entry for the current user.",
        "operationId": "PrinterMaintenance_PostPrinterMaintenanceEntry",
        "requestBody": {
          "description": "The dto containing all of the details for the printer maintenance entry to create.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.AddPrinterMaintenanceDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.AddPrinterMaintenanceDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.AddPrinterMaintenanceDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The filament detail DTO that was created.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the request is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/PrinterMaintenance/{id}": {
      "get": {
        "tags": [
          "PrinterMaintenance"
        ],
        "summary": "Returns detailed information for a maintence entry",
        "operationId": "PrinterMaintenance_GetMaintenanceEntry",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The GUID of the entry.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The Maintenance Entry details on success.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
                }
              }
            }
          },
          "404": {
            "description": "Returned when an entry with that GUID does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Returned when the requested entry does not belong to the current user."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "PrinterMaintenance"
        ],
        "summary": "Update an existing maintenance entry with new information",
        "operationId": "PrinterMaintenance_PutPrinterMaintenanceEntry",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The GUID of the entry to update.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "The updated entry details.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PutPrinterMaintenanceDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PutPrinterMaintenanceDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PutPrinterMaintenanceDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The updated entry information."
          },
          "400": {
            "description": "Returned if the id does not match the id of the entry details provided.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if a entry with the specified ID is not found.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the request is not authenticated."
          },
          "403": {
            "description": "Returned if the current user tries to update an entry which is not theirs."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "PrinterMaintenance"
        ],
        "summary": "Permantently delete a maintenance entry",
        "operationId": "PrinterMaintenance_DeletePrinterMaintenceEntry",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the entry to delete.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Returned if the entry was deleted successfully."
          },
          "403": {
            "description": "Returned if the current user cannot access the filament.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/PrinterMaintenance/categories": {
      "get": {
        "tags": [
          "PrinterMaintenance"
        ],
        "summary": "Returns a DTO which includes a list of all maintenance categories for the current user.",
        "operationId": "PrinterMaintenance_GetPrinterMaintenanceCategories",
        "responses": {
          "200": {
            "description": "The Maintence Categories on success.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.PrinterMaintenanceCategoriesDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.PrinterMaintenanceCategoriesDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.PrinterMaintenanceCategoriesDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/summary": {
      "get": {
        "tags": [
          "Printers"
        ],
        "summary": "List the current user's printers.",
        "description": "Inactive printers are left out unless `includeInactive=true`. Use a printer's `id` as the\r\n`printerId` when creating a print.",
        "operationId": "Printers_GetPrinterSummary",
        "parameters": [
          {
            "name": "PageNumber",
            "in": "query",
            "description": "The 1-based page number for pagination.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PageSize",
            "in": "query",
            "description": "The number of items to return per page.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "searchText",
            "in": "query",
            "description": "Filter printers by name, make, and model.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "includeInactive",
            "in": "query",
            "description": "By default, only returns active printers. Set this to true to return both active and inactive printers.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returned with a paged list of printer summaries.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummarySimpleDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummarySimpleDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummarySimpleDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/{id}": {
      "get": {
        "tags": [
          "Printers"
        ],
        "summary": "Return a specific printer by id.",
        "operationId": "Printers_GetPrinter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the printer.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returned with the printer details.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the current user cannot access the requested printer.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if the printer does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Printers"
        ],
        "summary": "Replace a printer's details.",
        "description": "Replaces the whole printer rather than patching it: send every field, not only the changed ones.",
        "operationId": "Printers_PutPrinter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the printer to update.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "description": "The updated printer details.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.AddPrinterDTO"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.AddPrinterDTO"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.AddPrinterDTO"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Returned with the updated printer details."
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the current user cannot access the requested printer.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if the printer does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Returned if the printer details do not contain all required fields, or if the ID in the printer details does not match the id in the route."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Printers"
        ],
        "summary": "Permantently delete a Printer, if the Printer has not been used in any existing prints.",
        "operationId": "Printers_DeletePrinter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the printer to delete.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Returned if the printer was deleted successfully."
          },
          "400": {
            "description": "Returned if the printer is unable to be deleted since it has been used in a print.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the current user cannot access the printer.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers": {
      "post": {
        "tags": [
          "Printers"
        ],
        "summary": "Create a new printer for the current user.",
        "operationId": "Printers_PostPrinter",
        "requestBody": {
          "description": "The printer details to create",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.AddPrinterDTO"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.AddPrinterDTO"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.AddPrinterDTO"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Returned with the newly creeated printer details.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Returned if the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the current user cannot access the requested printer.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if the printer does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Returned if the printer details do not contain all required fields, or if the ID in the printer details does not match the id in the route."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/{id}/filament": {
      "get": {
        "tags": [
          "Printers"
        ],
        "summary": "Retrieve the list of currently loaded filament for this printer",
        "operationId": "Printers_GetLoadedFilament",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterFilamentSummaryDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterFilamentSummaryDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterFilamentSummaryDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/{id}/filament/unload": {
      "put": {
        "tags": [
          "Printers"
        ],
        "summary": "Unload all filament for a printer by ID",
        "operationId": "Printers_UnloadPrinterFilament",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/thumbnails": {
      "get": {
        "tags": [
          "Printers"
        ],
        "summary": "Signed default-image thumbnails for every printer the caller owns.",
        "description": "Deliberately has no anonymous variant. Surfaces that merely mention a printer read\r\nthis map instead of carrying a signed URL on their own DTO, which is what keeps\r\nprinter photos off public print pages without a guard on each of those surfaces.",
        "operationId": "Printers_GetPrinterThumbnails",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterThumbnailDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterThumbnailDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterThumbnailDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/{id}/images": {
      "post": {
        "tags": [
          "Printers"
        ],
        "summary": "Upload a photo of a printer.",
        "description": "The uploaded bytes are decoded server-side; the declared content type from the client\r\nis not trusted. An undecodable or disallowed file is rejected before anything is\r\nstored.",
        "operationId": "Printers_PostPrinterImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored image, with signed URLs.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterImageDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterImageDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterImageDto"
                }
              }
            }
          },
          "400": {
            "description": "The file is missing, too large, or not a supported image.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "No such printer belonging to the current user.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/{id}/images/{imageId}": {
      "get": {
        "tags": [
          "Printers"
        ],
        "summary": "Redirects to a signed URL for a printer image.",
        "description": "For non-browser API consumers holding a bearer token. The UI does not use this - it\r\nreads pre-signed URLs from the printer DTO and the thumbnail map.\r\nNOT usable from <img src>: the redirect itself requires the bearer token.",
        "operationId": "Printers_GetPrinterImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Found"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Printers"
        ],
        "summary": "Deletes a printer image, its file rows, and its blobs.",
        "operationId": "Printers_DeletePrinterImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/{id}/images/reorder": {
      "put": {
        "tags": [
          "Printers"
        ],
        "summary": "Reorders the images on a printer. The supplied IDs must be its exact image set.",
        "operationId": "Printers_ReorderPrinterImages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "text/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "application/*+json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Printers/{id}/images/{imageId}/set-as-default": {
      "post": {
        "tags": [
          "Printers"
        ],
        "summary": "Makes one of the images on a printer its default.",
        "operationId": "Printers_SetDefaultPrinterImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/summary": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "List prints, paged, with optional search, filters and sorting.",
        "description": "Without `userId`, lists the authenticated user's own prints. With `userId`, lists that user's\r\npublic prints, and needs no credentials. Results are paged with `pageNumber` (from 1) and\r\n`pageSize` (default 10).\r\n            \r\nStatuses are integers: `1` Pending, `2` Printing, `3` Success, `4` Cancelled, `5` Failed,\r\n`6` PartialSuccess. `fromDate` and `toDate` must be supplied together.",
        "operationId": "Prints_GetPrintSummary",
        "parameters": [
          {
            "name": "PageNumber",
            "in": "query",
            "description": "The 1-based page number for pagination.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PageSize",
            "in": "query",
            "description": "The number of items to return per page.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "searchText",
            "in": "query",
            "description": "Optionally search for text in a print's title or notes.",
            "schema": {
              "maxLength": 50,
              "type": "string"
            }
          },
          {
            "name": "filterByPrinterIds",
            "in": "query",
            "description": "Optionally filter by specific printer ids.",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "SortDirection",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortDirection"
            }
          },
          {
            "name": "SortColumn",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortEnums.PrintSummarySortColumn"
            }
          },
          {
            "name": "filterByFilamentIds",
            "in": "query",
            "description": "Optionally filter by specific filament ids.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "filterByStatus",
            "in": "query",
            "description": "Optionally filter by a specific print status. PrintLogApi.Models.Print.PrintStatus",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
            }
          },
          {
            "name": "userId",
            "in": "query",
            "description": "Optionally search for public",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "filterByProjectId",
            "in": "query",
            "description": "Optionally filter prints belonging to a specific project.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "filterByStatuses",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
              }
            }
          },
          {
            "name": "filterByProjectIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A Paged List of Print Summaries matching the search criteria.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.PrintSummaryDTO"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.PrintSummaryDTO"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.PrintSummaryDTO"
                }
              }
            }
          },
          "400": {
            "description": "Returned if no user is logged in, and no userId is provided.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/stats": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Get Print Statistics for the current user.",
        "operationId": "Prints_GetPrintStats",
        "parameters": [
          {
            "name": "fromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintStatistic"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintStatistic"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintStatistic"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/grouped": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Returns a chronologically interleaved list of project rows and standalone print rows for the current user,\r\nwith optional filtering and sorting.",
        "operationId": "Prints_GetGrouped",
        "parameters": [
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 20
            }
          },
          {
            "name": "searchText",
            "in": "query",
            "schema": {
              "maxLength": 50,
              "type": "string"
            }
          },
          {
            "name": "filterByPrinterIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "filterByFilamentIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "filterByStatus",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
            }
          },
          {
            "name": "SortDirection",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortDirection"
            }
          },
          {
            "name": "SortColumn",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.SortEnums.PrintSummarySortColumn"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.GroupedFeedItemDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.GroupedFeedItemDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.GroupedFeedItemDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Get a print's full detail by id.",
        "description": "Public and unlisted prints can be read without credentials; a private print only by its creator.\r\nDurations are in seconds and filament amounts in milligrams.",
        "operationId": "Prints_GetPrintById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The id of a print to query",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the Print's Detailed information.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              }
            }
          },
          "404": {
            "description": "Returned when a print with that ID does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned when the current user (authenticated or not) cannot access the requested print id.\r\nNormally when the print is marked as private, and the current user cannot access it."
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Prints"
        ],
        "summary": "Replace a print's details.",
        "description": "Replaces the whole print rather than patching it: read the print with `GET /api/Prints/{id}`,\r\nchange the fields you need, and send the whole object back. The last request wins.",
        "operationId": "Prints_PutPrint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print to update.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "description": "The new print detail information.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PutPrintDetailDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PutPrintDetailDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PutPrintDetailDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The newly-updated Print Detail information.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              }
            }
          },
          "404": {
            "description": "Returned when the printDTO is not valid, or if the printDTO's ID does not match the ID in the route.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned when no user is authenticated."
          },
          "403": {
            "description": "Returned when the current authenticated user does not have access to update the requested print."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Prints"
        ],
        "summary": "Delete a print permanently. Deleted prints will delete any associated print data, such as comments, filament usage, images, etc.",
        "operationId": "Prints_DeletePrint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The id of the print to delete.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "When the print was deleted successfully.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              }
            }
          },
          "403": {
            "description": "Returned when the current user does not have access to delete/modify the requested print.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if there is no print with the requested id.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned when the user is unauthorized."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/csv": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Generate a print report for the current user.",
        "operationId": "Prints_GetAllPrintDetailsAsCsv",
        "responses": {
          "200": {
            "description": "Returns a octet-stream containing a comma-separated value (.csv) file with a report of the current user's print information."
          },
          "401": {
            "description": "Returned when no user is currently authenticated."
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}/status/{newStatus}": {
      "put": {
        "tags": [
          "Prints"
        ],
        "summary": "Update a print with a new PrintStatus.",
        "description": "Changes only the status, leaving every other field as it is. Statuses: `1` Pending,\r\n`2` Printing, `3` Success, `4` Cancelled, `5` Failed, `6` PartialSuccess.",
        "operationId": "Prints_UpdatePrintStatus",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print to update.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "newStatus",
            "in": "path",
            "description": "The new Print Status.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The updated Print Detail information.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the currently authenticated user does not have access to update the requested print.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned if no print is found with the requested id.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/bulk-update": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Apply one set of field values to many prints in a single request.",
        "description": "Every field is optional and omitted fields are left untouched. Enum values are\r\nintegers. The request is a 200 even when individual ids could not be acted on;\r\nthe response body reports each id as succeeded or failed.",
        "operationId": "Prints_BulkUpdatePrints",
        "requestBody": {
          "description": "The ids to update and the values to apply.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkUpdatePrintsDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkUpdatePrintsDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkUpdatePrintsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The per-id outcome of the operation.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkPrintResultDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkPrintResultDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkPrintResultDto"
                }
              }
            }
          },
          "400": {
            "description": "The request as a whole was invalid. Nothing was written.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned when no user is authenticated."
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Create a new Print.",
        "description": "`title` is required, and `printerId` is the id of one of the caller's printers. Durations are\r\nin seconds and filament amounts in milligrams. `status` is an integer (`1` Pending,\r\n`2` Printing, `3` Success, `4` Cancelled, `5` Failed, `6` PartialSuccess), and `viewStatus`\r\nis `1` Public, `2` Unlisted or `3` Private.",
        "operationId": "Prints_PostPrint",
        "requestBody": {
          "description": "The print details to create.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.AddPrintDTO"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.AddPrintDTO"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.AddPrintDTO"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Returned if the create was successful, containing the new Print Detail information.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintDetailDTO"
                }
              }
            }
          },
          "400": {
            "description": "Returned if the new Print is not valid. Inspect Problem Details object for message as to what failed validation.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/bulk-delete": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Permanently delete many prints in a single request.",
        "description": "Ids that no longer exist are reported as succeeded - the goal state is that the\r\nprint is gone. Only the print's creator may delete it.",
        "operationId": "Prints_BulkDeletePrints",
        "requestBody": {
          "description": "The ids to delete.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkDeletePrintsDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkDeletePrintsDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkDeletePrintsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The per-id outcome of the operation.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkPrintResultDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkPrintResultDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkPrintResultDto"
                }
              }
            }
          },
          "400": {
            "description": "The request as a whole was invalid. Nothing was deleted.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Returned when no user is authenticated."
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{printId}/image/{imageId}": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Gets an image attached to a print.",
        "operationId": "Prints_GetImage",
        "parameters": [
          {
            "name": "printId",
            "in": "path",
            "description": "The Id of the print.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "description": "The id of the image to retrieve.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Prints"
        ],
        "summary": "Delete an image from a print. If the deleted image was the default, the next image by DisplayOrder is promoted.",
        "operationId": "Prints_RemoveImage",
        "parameters": [
          {
            "name": "printId",
            "in": "path",
            "description": "The id of the print.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "description": "The id of the image to remove.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{printId}/image/{imageId}/set-as-default": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Prints have a single \"default\" image, which is the image used in thumbnails. This is used to mark a specific image as the default image.",
        "operationId": "Prints_SetImageAsDefault",
        "parameters": [
          {
            "name": "printId",
            "in": "path",
            "description": "The id of the print.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "description": "The id of the image to make default.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{printId}/images/reorder": {
      "put": {
        "tags": [
          "Prints"
        ],
        "summary": "Reorder the images attached to a print by assigning a display order to each image.",
        "description": "This endpoint requires a <strong>complete</strong> set of image IDs for the print.\r\nEvery image currently attached to the print must be included in reorderDto —\r\npartial updates (supplying only a subset of IDs, or including extra IDs) are not supported.\r\nIf the supplied set of image IDs does not exactly match the images belonging to the print,\r\nthe request is rejected with a 400 Bad Request response.",
        "operationId": "Prints_ReorderImages",
        "parameters": [
          {
            "name": "printId",
            "in": "path",
            "description": "The id of the print whose images are being reordered.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "description": "The complete list of image IDs and their new display order values.\r\nMust contain every image ID that belongs to the print — no more, no less.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.ReorderImagesDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.ReorderImagesDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.ReorderImagesDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The images were successfully reordered."
          },
          "400": {
            "description": "Returned when the supplied image ID set does not exactly match the images attached to the print,\r\nor when the images list is null or empty.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Returned when the authenticated user does not own the requested print.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Returned when no print is found with the given printId.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}/image": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Create a new image and attach it to an existing print. If the \"isDefault\" param is set to true, then\r\nit will mark the image as the print's default image.",
        "operationId": "Prints_PostImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print to save the image to.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string",
                    "description": "The image file to save.",
                    "format": "binary"
                  },
                  "isDefault": {
                    "type": "boolean",
                    "description": "If true, then mark the new image as the print's default image.",
                    "default": false
                  }
                }
              },
              "encoding": {
                "image": {
                  "style": "form"
                },
                "isDefault": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintImageDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintImageDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintImageDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{printId}/comment": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Create a new comment and attach it to the print.",
        "operationId": "Prints_PostPrintComment",
        "parameters": [
          {
            "name": "printId",
            "in": "path",
            "description": "The ID of the print to comment on.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "description": "The details of the new comment.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.AddCommentDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.AddCommentDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.AddCommentDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "An OK response with the new comment details if the comment was added successfully.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the user cannot access the print specified.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "400": {
            "description": "Returned if the request contains bad details, or if the print specified does not allow comments."
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{printId}/comment/{commentId}": {
      "delete": {
        "tags": [
          "Prints"
        ],
        "summary": "Delete a Comment from a print. The owner of the Print can remove any comment on that print, while other users can only delete comments they created.",
        "operationId": "Prints_DeletePrintComment",
        "parameters": [
          {
            "name": "printId",
            "in": "path",
            "description": "The print ID",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "commentId",
            "in": "path",
            "description": "The comment Id to delete.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An OK response if the deletion was successful.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
                }
              }
            }
          },
          "403": {
            "description": "Returned if the user is not the print owner or the owner of the comment.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/public": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Returns an array of all the IDs for public prints, for use with creating and updating sitemaps.",
        "operationId": "Prints_GetPublicPrintIds",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/public/sitemap": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Returns every public print's id with its last-modified time, for the sitemap's lastmod.",
        "description": "A sibling of `GET public` rather than a change to it, so a sitemap generator that still\r\nexpects a bare id array keeps working. `lastModified` is the print's own last save\r\n(UTC) and is null when no trustworthy timestamp exists.",
        "operationId": "Prints_GetPublicPrintSitemapEntries",
        "responses": {
          "200": {
            "description": "The public prints, ordered by id.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Sitemap.SitemapEntryDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Sitemap.SitemapEntryDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Sitemap.SitemapEntryDto"
                  }
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}/files/upload-url": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Generates a SAS URL for uploading a file directly to blob storage. Pro subscription required.",
        "operationId": "Prints_GetFileUploadUrl",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print to attach the file to.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "description": "Details about the file to be uploaded.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetUploadUrlRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetUploadUrlRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetUploadUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Returns the SAS upload URL and blob path.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetUploadUrlResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetUploadUrlResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetUploadUrlResponse"
                }
              }
            }
          },
          "400": {
            "description": "If the file type is not supported or quota is exceeded.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "If the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "If the user does not own the print or does not have a Pro subscription.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "If the print does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}/files/confirm": {
      "post": {
        "tags": [
          "Prints"
        ],
        "summary": "Confirms that a file has been uploaded to blob storage and records it as an attachment on the print.\r\nPro subscription required.",
        "operationId": "Prints_ConfirmFileUpload",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print the file was uploaded for.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "description": "The blob path and file metadata returned from the upload-url step.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.ConfirmUploadRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.ConfirmUploadRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.ConfirmUploadRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Returns the created PrintAttachment details.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintAttachmentDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintAttachmentDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintAttachmentDto"
                }
              }
            }
          },
          "400": {
            "description": "If the blob path is invalid.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "If the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "If the user does not own the print or does not have a Pro subscription.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "If the print does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}/files": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Returns all file attachments for a print.\r\nAnonymous access is permitted for public prints; private prints require the owner to be authenticated.",
        "operationId": "Prints_GetFiles",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print whose attachments to retrieve.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the list of file attachments (may be empty).",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintAttachmentDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintAttachmentDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintAttachmentDto"
                  }
                }
              }
            }
          },
          "403": {
            "description": "If the print is private and the requesting user is not the owner.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "If the print does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}/files/{fileId}/download-url": {
      "get": {
        "tags": [
          "Prints"
        ],
        "summary": "Generates a time-limited SAS download URL for a file attachment.\r\nAnonymous access is permitted when the print has file downloads enabled; otherwise the owner must be authenticated.",
        "operationId": "Prints_GetFileDownloadUrl",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print the file is attached to.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "fileId",
            "in": "path",
            "description": "The ID of the attachment to download.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the download URL and its expiry time.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetDownloadUrlResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetDownloadUrlResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GetDownloadUrlResponse"
                }
              }
            }
          },
          "403": {
            "description": "If file downloads are not enabled for this print and the user is not the owner.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "If the print or attachment does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Prints/{id}/files/{fileId}": {
      "delete": {
        "tags": [
          "Prints"
        ],
        "summary": "Deletes a file attachment from a print. Only the owner of the attachment may delete it.",
        "operationId": "Prints_DeleteFile",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the print the file is attached to.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "fileId",
            "in": "path",
            "description": "The ID of the attachment to delete.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "If the file was deleted successfully."
          },
          "401": {
            "description": "If the user is not authenticated.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "If the user does not own the file attachment.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "If the print or attachment does not exist.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get a paged list of the current user's projects.",
        "operationId": "Projects_GetProjects",
        "parameters": [
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 10
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectStatus"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "updatedDate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Project.ProjectSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Project.ProjectSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Project.ProjectSummaryDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Create a new project.",
        "operationId": "Projects_PostProject",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.AddProjectDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.AddProjectDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.AddProjectDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Projects/{id}": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get a project's full detail by ID. Public/Unlisted projects are accessible without auth.",
        "operationId": "Projects_GetProjectById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Projects"
        ],
        "summary": "Update a project's metadata.",
        "operationId": "Projects_PutProject",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.PutProjectDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.PutProjectDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.PutProjectDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectDetailDto"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Projects"
        ],
        "summary": "Delete a project. If deletePrints=true, also deletes member prints.\r\nIf deletePrints=false (default), prints are unlinked and become standalone.",
        "operationId": "Projects_DeleteProject",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "deletePrints",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Projects/{id}/images/{imageId}": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get a project image.",
        "operationId": "Projects_GetProjectImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Projects"
        ],
        "summary": "Remove an image from a project.",
        "operationId": "Projects_DeleteProjectImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Projects/{id}/images": {
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Upload an image to a project.",
        "operationId": "Projects_PostProjectImage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectImageDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectImageDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectImageDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Projects/{id}/images/reorder": {
      "put": {
        "tags": [
          "Projects"
        ],
        "summary": "Reorder a project's images.",
        "operationId": "Projects_ReorderProjectImages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "text/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "application/*+json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Projects/{id}/images/{imageId}/set-as-default": {
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Set a project image as the default.",
        "operationId": "Projects_SetProjectImageAsDefault",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "imageId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Subscription/me": {
      "get": {
        "tags": [
          "Subscription"
        ],
        "summary": "Get the current user's subscription status.",
        "operationId": "Subscription_GetCurrentUserSubscription",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Subscription.SubscriptionDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Subscription.SubscriptionDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Subscription.SubscriptionDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Subscription/checkout": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Create a Stripe Checkout session for upgrading to Pro.",
        "operationId": "Subscription_CreateCheckoutSession",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Subscription.CreateCheckoutSessionDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Subscription.CreateCheckoutSessionDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Subscription.CreateCheckoutSessionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Controllers.CheckoutSessionResponseDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Controllers.CheckoutSessionResponseDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Controllers.CheckoutSessionResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Subscription/portal": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Create a Stripe Customer Portal session for managing billing.",
        "operationId": "Subscription_CreatePortalSession",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Controllers.PortalSessionResponseDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Controllers.PortalSessionResponseDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Controllers.PortalSessionResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Subscription/webhook": {
      "post": {
        "tags": [
          "Subscription"
        ],
        "summary": "Stripe webhook endpoint. Validates the Stripe-Signature header and processes events.",
        "operationId": "Subscription_HandleWebhook",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/UserApiKeys": {
      "get": {
        "tags": [
          "UserApiKeys"
        ],
        "summary": "Get the list of API Key summary info for the current user.",
        "operationId": "UserApiKeys_GetApiKeySummaryForUser",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.UserApiKeyDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.UserApiKeyDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.UserApiKeyDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "UserApiKeys"
        ],
        "summary": "Generate a new API key for the current user.",
        "operationId": "UserApiKeys_GenerateNewApiKey",
        "requestBody": {
          "description": "",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.AddNewApiKeyDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.AddNewApiKeyDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.AddNewApiKeyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.NewUserApiKeyDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.NewUserApiKeyDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.NewUserApiKeyDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/UserApiKeys/{apiKey}": {
      "delete": {
        "tags": [
          "UserApiKeys"
        ],
        "summary": "Delete the specific API Key.",
        "operationId": "UserApiKeys_DeleteApiKey",
        "parameters": [
          {
            "name": "apiKey",
            "in": "path",
            "description": "",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.NewUserApiKeyDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.NewUserApiKeyDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserApiKeys.NewUserApiKeyDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/{id}/summary": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Get the user summary for the specified user id.",
        "operationId": "Users_GetUserSummary",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The ID of the user to retrieve.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/me": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Gets the user details for the currently authenticated user.",
        "operationId": "Users_GetCurrentUserDetails",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Updates the current user's details.",
        "operationId": "Users_UpdateCurrentUserDetails",
        "requestBody": {
          "description": "",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UpdateUserDetailDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UpdateUserDetailDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UpdateUserDetailDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/me/email": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "The address email is sent to, as last synced from the sign-in provider. Interactive\r\nsessions only: an API key must not be a way to read the account address.",
        "operationId": "Users_GetAccountEmail",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Email.AccountEmailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Email.AccountEmailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Email.AccountEmailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          }
        ]
      }
    },
    "/api/Users/me/deactivate": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Marks the current user as deactivated",
        "operationId": "Users_DeactivateCurrentUser",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/me/reactivate": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Reactivate the current user if the user has not yet been deleted.",
        "operationId": "Users_ReactivateCurrentUser",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/pending-deactivation": {
      "delete": {
        "tags": [
          "Users"
        ],
        "summary": "Delete the data from users pending deactivation after the deactivation period.",
        "operationId": "Users_ProcessPendingDeactivations",
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/{id}": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Gets the user details of the specified user. Respects the user's Profile View Status, so private profiles cannot be accessed by other users.",
        "operationId": "Users_GetUserDetails",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Id of the user to retrieve.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserDetailDto"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/me/profile-image": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Replace the user profile image of the currently authenticated user.",
        "operationId": "Users_PostProfileImage",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string",
                    "description": "The image file to save.",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "image": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/me/cover-image": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Replace the user cover image of the currently authenticated user.",
        "operationId": "Users_PostCoverImage",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string",
                    "description": "The image file to save.",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "image": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Users"
        ],
        "summary": "Remove the current user's cover-image.",
        "operationId": "Users_RemoveCoverImage",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserUrlDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/public": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Returns an array of all the IDs for public users, for use with creating and updating sitemaps.",
        "operationId": "Users_GetPublicUserIds",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/me/user-settings": {
      "get": {
        "tags": [
          "UserSettings"
        ],
        "summary": "Returns the list of the current user's UserSettings.",
        "operationId": "UserSettings_GetCurrentUsersSettings",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "UserSettings"
        ],
        "summary": "Update an existing user setting.",
        "operationId": "UserSettings_UpdateUserSetting",
        "requestBody": {
          "description": "The details of the user setting to update.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UpdateUserSettingDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UpdateUserSettingDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UpdateUserSettingDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "UserSettings"
        ],
        "summary": "Save a new user setting.",
        "operationId": "UserSettings_CreateUserSetting",
        "requestBody": {
          "description": "",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.AddUserSettingDto"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.AddUserSettingDto"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.AddUserSettingDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.UserSetting.UserSettingDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/{userId}/total-filament-usage": {
      "get": {
        "tags": [
          "UsersPrints"
        ],
        "summary": "Get the total amount of filament used by a user between a date range.",
        "operationId": "UsersPrints_GetUsersTotalFilamentUsage",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "description": "The user Id to retrieve",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "description": "The datetime of the start of the date range (inclusive)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "description": "The datetime of the end of the date range (inclusive)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/{userId}/print-count": {
      "get": {
        "tags": [
          "UsersPrints"
        ],
        "summary": "Get the total number of prints by a user between a date range.",
        "operationId": "UsersPrints_GetUsersTotalPrintCount",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "description": "The user Id to retrieve",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "description": "The datetime of the start of the date range (inclusive)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "description": "The datetime of the end of the date range (inclusive)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    },
    "/api/Users/{userId}/total-print-time": {
      "get": {
        "tags": [
          "UsersPrints"
        ],
        "summary": "Get the total print time by a user between a date range.",
        "operationId": "UsersPrints_GetUsersTotalPrintTimeInSeconds",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "description": "The user Id to retrieve",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "description": "The datetime of the start of the date range (inclusive)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "description": "The datetime of the end of the date range (inclusive)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.SinglePrintStat"
                }
              }
            }
          }
        },
        "security": [
          { },
          {
            "oauth2": [ ]
          },
          {
            "apikey": [ ]
          },
          {
            "apikeyQuery": [ ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Microsoft.AspNetCore.Mvc.ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "instance": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": { }
      },
      "PrintLogApi.Achievements.AchievementCategory": {
        "enum": [
          1,
          2,
          3,
          4,
          5,
          6
        ],
        "type": "integer",
        "description": "Catalog groupings. Serialized as numbers; the UI mirrors these values.",
        "format": "int32"
      },
      "PrintLogApi.Controllers.CheckoutSessionResponseDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Controllers.PortalSessionResponseDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Email.AccountEmailDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "nullable": true
          },
          "verified": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Enums.ColorPatternType": {
        "enum": [
          1,
          2,
          3,
          4
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Enums.FilamentEffect": {
        "enum": [
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Enums.FilamentFinishType": {
        "enum": [
          1,
          2,
          3
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.CuraSetting": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "curaVersion": {
            "maxLength": 100,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "pluginVersion": {
            "maxLength": 100,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "settings": { },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "userId": {
            "type": "integer",
            "description": "The ID of the user that first loaded this setting, thus locking it to that user.",
            "format": "int64",
            "nullable": true
          },
          "slicer": {
            "maxLength": 50,
            "minLength": 0,
            "type": "string",
            "description": "The slicer name the Slic3r uploader posts (its parser's `SlicerName`). Null means the\r\nCura plugin, which does not send the field. Copied onto the print when one is created.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.AchievementCatalogDto": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer",
            "format": "int32"
          },
          "hiddenCount": {
            "type": "integer",
            "description": "How many hidden families exist. Their keys and copy are never sent.",
            "format": "int32"
          },
          "families": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementFamilyDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.AchievementFamilyDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "$ref": "#/components/schemas/PrintLogApi.Achievements.AchievementCategory"
          },
          "glyph": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "hintOrder": {
            "type": "integer",
            "format": "int32"
          },
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementTierDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.AchievementProgressDto": {
        "type": "object",
        "properties": {
          "best": {
            "type": "integer",
            "description": "Never below the threshold of the highest tier held, even after prints are deleted.",
            "format": "int64"
          },
          "current": {
            "type": "integer",
            "format": "int64"
          },
          "nextThreshold": {
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.AchievementTierDto": {
        "type": "object",
        "properties": {
          "tier": {
            "type": "integer",
            "format": "int32"
          },
          "threshold": {
            "type": "integer",
            "format": "int64"
          },
          "description": {
            "type": "string",
            "description": "The tier's description with its threshold filled in.",
            "nullable": true
          },
          "rarityPercent": {
            "type": "number",
            "description": "Share of evaluated makers holding this tier, 0–100.",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.DismissHintRequestDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "tier": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "Request body for dismissing the hint card."
      },
      "PrintLogApi.Models.DTOs.Achievements.EarnedTierDto": {
        "type": "object",
        "properties": {
          "tier": {
            "type": "integer",
            "format": "int32"
          },
          "unlockedAt": {
            "type": "string",
            "format": "date-time"
          },
          "retroactive": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.MyAchievementFamilyDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.EarnedTierDto"
            },
            "nullable": true
          },
          "progress": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementProgressDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.MyAchievementsDto": {
        "type": "object",
        "properties": {
          "earnedTierCount": {
            "type": "integer",
            "format": "int32"
          },
          "totalTierCount": {
            "type": "integer",
            "format": "int32"
          },
          "families": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.MyAchievementFamilyDto"
            },
            "nullable": true
          },
          "revealedHidden": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementFamilyDto"
            },
            "description": "Full catalog entries for the hidden families this user has earned.",
            "nullable": true
          },
          "nextHint": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.NextHintDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.NextHintDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "tier": {
            "type": "integer",
            "format": "int32"
          },
          "ctaRoute": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.PublicAchievementsDto": {
        "type": "object",
        "properties": {
          "earnedTierCount": {
            "type": "integer",
            "format": "int32"
          },
          "featured": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.PublicFamilyDto"
            },
            "description": "Up to four families: highest tier, then rarest, then most recent.",
            "nullable": true
          },
          "families": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.PublicFamilyDto"
            },
            "nullable": true
          },
          "revealedHidden": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.AchievementFamilyDto"
            },
            "description": "Catalog entries for earned hidden families; the owner revealed them by earning them.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Achievements.PublicFamilyDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "highestTier": {
            "type": "integer",
            "format": "int32"
          },
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Achievements.EarnedTierDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One earned family on a public profile: earned tiers only, never progress."
      },
      "PrintLogApi.Models.DTOs.Analytics.AccuracyCallout": {
        "type": "object",
        "properties": {
          "scope": {
            "type": "string",
            "nullable": true
          },
          "key": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "dimension": {
            "type": "string",
            "nullable": true
          },
          "medianRatio": {
            "type": "number",
            "format": "double"
          },
          "sampleSize": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "Structured facts, never a sentence. The client composes \"your Ender 3 runs about 18%\r\nlonger than estimated\" — plain language belongs where units and translation already live."
      },
      "PrintLogApi.Models.DTOs.Analytics.AccuracyGroup": {
        "type": "object",
        "properties": {
          "scope": {
            "type": "string",
            "nullable": true
          },
          "key": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "medianRatio": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "sampleSize": {
            "type": "integer",
            "format": "int32"
          },
          "suppressedForSmallSample": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.AccuracyResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timeZone": {
            "type": "string",
            "nullable": true
          },
          "granularity": {
            "type": "string",
            "nullable": true
          },
          "timeAccuracyMedian": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "materialAccuracyMedian": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "timeScatter": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Services.Analytics.ScatterBin"
            },
            "nullable": true
          },
          "byPrinter": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AccuracyGroup"
            },
            "nullable": true
          },
          "byMaterial": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AccuracyGroup"
            },
            "nullable": true
          },
          "biasTrend": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AccuracyTrendBucket"
            },
            "nullable": true
          },
          "callouts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.AccuracyCallout"
            },
            "nullable": true
          },
          "coverage": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Coverage"
          }
        },
        "additionalProperties": false,
        "description": "The shape NEVER varies by viewport. The phone renders by-printer bars instead of the\r\nscatter from this same payload; a viewport-dependent response would break caching and\r\ntyped clients (spec §11)."
      },
      "PrintLogApi.Models.DTOs.Analytics.AccuracyTrendBucket": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int32"
          },
          "localStart": {
            "type": "string",
            "format": "date"
          },
          "medianRatio": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "sampleSize": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.ActivityResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timeZone": {
            "type": "string",
            "nullable": true
          },
          "granularity": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.ActivitySeriesBucket"
            },
            "nullable": true
          },
          "calendar": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CalendarDay"
            },
            "nullable": true
          },
          "calendarFrom": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "calendarTo": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "streaks": {
            "$ref": "#/components/schemas/PrintLogApi.Services.Analytics.StreakSummary"
          },
          "durationHistogram": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Services.Analytics.HistogramBucket"
            },
            "nullable": true
          },
          "startTimeMatrix": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Services.Analytics.MatrixCell"
            },
            "nullable": true
          },
          "coverage": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Coverage"
          }
        },
        "additionalProperties": false,
        "description": "The Activity tab payload. CalendarFrom/CalendarTo carry the window the calendar actually\r\ncovers, which may be narrower than the filter's, so the UI never has to infer it."
      },
      "PrintLogApi.Models.DTOs.Analytics.ActivitySeriesBucket": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int32"
          },
          "localStart": {
            "type": "string",
            "format": "date"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          },
          "durationSeconds": {
            "type": "integer",
            "format": "int64"
          },
          "materialMg": {
            "type": "integer",
            "format": "int64"
          },
          "cost": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One period, carrying EVERY metric the tab's toggle can show. Four metrics in one payload\r\nrather than four requests: the toggle is instant, and the four numbers are guaranteed to\r\ndescribe the same set of prints. Cost is null when the cost row cap was exceeded."
      },
      "PrintLogApi.Models.DTOs.Analytics.AnalyticsGranularity": {
        "enum": [
          0,
          1,
          2,
          3
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.DTOs.Analytics.CalendarDay": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.CostGroup": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "printCount": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.CostSeriesBucket": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int32"
          },
          "localStart": {
            "type": "string",
            "format": "date"
          },
          "filament": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "electricity": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "maintenance": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Components stay separate: they fail independently and the chart stacks them."
      },
      "PrintLogApi.Models.DTOs.Analytics.CostsResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timeZone": {
            "type": "string",
            "nullable": true
          },
          "granularity": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "totalSpend": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MoneyMetric"
          },
          "filamentSpend": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MoneyMetric"
          },
          "electricitySpend": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MoneyMetric"
          },
          "maintenanceSpend": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MoneyMetric"
          },
          "spendOverTime": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CostSeriesBucket"
            },
            "nullable": true
          },
          "costPerPrint": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Services.Analytics.HistogramBucket"
            },
            "nullable": true
          },
          "byMaterialType": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CostGroup"
            },
            "nullable": true
          },
          "byBrand": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CostGroup"
            },
            "nullable": true
          },
          "costOfFailure": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MoneyMetric"
          },
          "costOfFailureSharePercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "mostExpensive": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.PrintCostRef"
            },
            "nullable": true
          },
          "leastExpensive": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.PrintCostRef"
            },
            "nullable": true
          },
          "coverage": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Coverage"
          }
        },
        "additionalProperties": false,
        "description": "The Costs tab payload. CostOfFailureSharePercent is null when total spend is 0 — a share\r\nof nothing is undefined, not 0%, and 0% would read as \"no failures\"."
      },
      "PrintLogApi.Models.DTOs.Analytics.Coverage": {
        "type": "object",
        "properties": {
          "population": {
            "type": "string",
            "nullable": true
          },
          "counted": {
            "type": "integer",
            "format": "int32"
          },
          "total": {
            "type": "integer",
            "format": "int32"
          },
          "undatedCount": {
            "type": "integer",
            "format": "int32"
          },
          "exclusions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.CoverageExclusion"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Population is named explicitly (\"prints\", \"spools\", \"printers\") because a count of\r\nexcluded spools is not a count of excluded prints."
      },
      "PrintLogApi.Models.DTOs.Analytics.CoverageExclusion": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "nullable": true
          },
          "count": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.HighlightRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "value": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "unit": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A pointer to the entity behind a headline, so the UI can link to it."
      },
      "PrintLogApi.Models.DTOs.Analytics.MaintenanceEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "cost": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.MaterialGroup": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "printCount": {
            "type": "integer",
            "format": "int32"
          },
          "materialMg": {
            "type": "integer",
            "format": "int64"
          },
          "swatch": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.SwatchDto"
          }
        },
        "additionalProperties": false,
        "description": "PrintCount is DISTINCT prints for a real group, so \"most used\" ranks by prints rather\r\nthan by usage rows (spec §5).\r\n            \r\nThe synthetic \"Other\" rollup is the one exception: it sums its members' counts, so a\r\nprint spanning two rolled-up groups contributes twice. That is a count of group\r\nappearances, not of prints, and the row is labelled \"Other\" rather than carrying a claim\r\nabout any particular material — but the field means something slightly different there,\r\nand callers ranking or charting it should not treat it as a print count."
      },
      "PrintLogApi.Models.DTOs.Analytics.MaterialSeriesBucket": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int32"
          },
          "localStart": {
            "type": "string",
            "format": "date"
          },
          "materialMgByType": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "format": "int64"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.MaterialsResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timeZone": {
            "type": "string",
            "nullable": true
          },
          "granularity": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "byType": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaterialGroup"
            },
            "nullable": true
          },
          "byBrand": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaterialGroup"
            },
            "nullable": true
          },
          "byColor": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaterialGroup"
            },
            "nullable": true
          },
          "consumptionOverTime": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaterialSeriesBucket"
            },
            "nullable": true
          },
          "topSpools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.SpoolRow"
            },
            "nullable": true
          },
          "runway": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.RunwayRow"
            },
            "nullable": true
          },
          "wasteGrams": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "wasteCost": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MoneyMetric"
          },
          "coverage": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Coverage"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.Metric": {
        "type": "object",
        "properties": {
          "value": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "previous": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "coverage": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Coverage"
          }
        },
        "additionalProperties": false,
        "description": "A scalar metric. Previous is null unless comparePrevious was requested."
      },
      "PrintLogApi.Models.DTOs.Analytics.MoneyMetric": {
        "type": "object",
        "properties": {
          "value": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "previous": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "coverage": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Coverage"
          }
        },
        "additionalProperties": false,
        "description": "Money is decimal, never double, and always carries its currency."
      },
      "PrintLogApi.Models.DTOs.Analytics.OverviewHighlights": {
        "type": "object",
        "properties": {
          "mostUsedPrinter": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.HighlightRef"
          },
          "mostUsedMaterial": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.HighlightRef"
          },
          "longestPrint": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.HighlightRef"
          },
          "priciestPrint": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.HighlightRef"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.OverviewResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timeZone": {
            "type": "string",
            "nullable": true
          },
          "granularity": {
            "type": "string",
            "nullable": true
          },
          "tiles": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.OverviewTiles"
          },
          "statusBreakdown": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.StatusCount"
            },
            "nullable": true
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.SeriesBucket"
            },
            "nullable": true
          },
          "highlights": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.OverviewHighlights"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.OverviewTiles": {
        "type": "object",
        "properties": {
          "printCount": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "successRatePercent": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "filamentGrams": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "printTimeSeconds": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "totalCost": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MoneyMetric"
          },
          "avgPrintTimeSeconds": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.PrintCostRef": {
        "type": "object",
        "properties": {
          "printId": {
            "type": "integer",
            "format": "int64"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.PrinterRow": {
        "type": "object",
        "properties": {
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "isIdle": {
            "type": "boolean"
          },
          "printCount": {
            "type": "integer",
            "format": "int32"
          },
          "successRatePercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "printTimeSeconds": {
            "type": "integer",
            "format": "int64"
          },
          "materialMg": {
            "type": "integer",
            "format": "int64"
          },
          "avgDurationSeconds": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "cost": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "maintenanceCost": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "utilizationPercent": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "costPerPrintHour": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One printer's figures for the window. IsIdle is true when the printer produced nothing in\r\nrange — returned as a row rather than omitted, because \"this printer did nothing\" is the\r\nmost actionable line on the tab and a missing row says it silently."
      },
      "PrintLogApi.Models.DTOs.Analytics.PrinterSeriesBucket": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int32"
          },
          "localStart": {
            "type": "string",
            "format": "date"
          },
          "printSecondsByPrinterId": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "format": "int64"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Keyed by printer id as a string so it serializes as a JSON object, not an array."
      },
      "PrintLogApi.Models.DTOs.Analytics.PrintersResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timeZone": {
            "type": "string",
            "nullable": true
          },
          "granularity": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "printers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.PrinterRow"
            },
            "nullable": true
          },
          "timeSeries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.PrinterSeriesBucket"
            },
            "nullable": true
          },
          "fleetUtilizationPercent": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Metric"
          },
          "maintenance": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.MaintenanceEvent"
            },
            "nullable": true
          },
          "coverage": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.Coverage"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.RunwayRow": {
        "type": "object",
        "properties": {
          "filamentId": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "swatch": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.SwatchDto"
          },
          "remainingGrams": {
            "type": "number",
            "format": "double"
          },
          "burnRateGramsPerDay": {
            "type": "number",
            "format": "double"
          },
          "runwayDays": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.SeriesBucket": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int32"
          },
          "localStart": {
            "type": "string",
            "format": "date"
          },
          "countsByStatus": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.SpoolRow": {
        "type": "object",
        "properties": {
          "filamentId": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "swatch": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Analytics.SwatchDto"
          },
          "usedMg": {
            "type": "integer",
            "format": "int64"
          },
          "remainingMg": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "initialMg": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "percentConsumed": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "costConsumed": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "RemainingMg follows the canonical FilamentRemaining rule and may be NEGATIVE: that means\r\nusage was logged beyond the spool's initial weight, a real data problem the user should\r\nbe able to see and fix rather than one we quietly clamp away."
      },
      "PrintLogApi.Models.DTOs.Analytics.StatusCount": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "nullable": true
          },
          "count": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Analytics.SwatchDto": {
        "type": "object",
        "properties": {
          "colors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "colorPattern": {
            "type": "integer",
            "format": "int32"
          },
          "finishType": {
            "type": "integer",
            "format": "int32"
          },
          "effects": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A filament's appearance, sent raw. The UI turns it into CSS or SVG through its shared\r\nswatch descriptor — the server never emits presentation strings."
      },
      "PrintLogApi.Models.DTOs.Comments.AddCommentDto": {
        "type": "object",
        "properties": {
          "body": {
            "maxLength": 2000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Comments.CommentDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "body": {
            "type": "string",
            "nullable": true
          },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "createdById": {
            "type": "integer",
            "format": "int64"
          },
          "createdBy": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
          },
          "updatedDate": {
            "type": "string",
            "format": "date-time"
          },
          "updatedById": {
            "type": "integer",
            "format": "int64"
          },
          "updatedBy": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Comments.EditCommentDto": {
        "type": "object",
        "properties": {
          "body": {
            "maxLength": 2000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.ConnectedAgentDto": {
        "type": "object",
        "properties": {
          "grantId": {
            "type": "string",
            "nullable": true
          },
          "clientId": {
            "type": "string",
            "nullable": true
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A connected AI agent, backed by an Auth0 grant for the dedicated MCP audience. v1 exposes\r\nonly fields the Auth0 grants response supplies."
      },
      "PrintLogApi.Models.DTOs.CuraSettings.NewCuraSettingsDto": {
        "type": "object",
        "properties": {
          "newSettingId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Device.RegisterDeviceDto": {
        "required": [
          "platform",
          "token"
        ],
        "type": "object",
        "properties": {
          "token": {
            "maxLength": 512,
            "minLength": 1,
            "type": "string"
          },
          "platform": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DevicePlatform"
          },
          "appVersion": {
            "maxLength": 50,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Feedback.AddFeedbackDto": {
        "type": "object",
        "properties": {
          "type": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Feedback_FeedbackType"
          },
          "email": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "note": {
            "maxLength": 5000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.AddFilamentDto": {
        "type": "object",
        "properties": {
          "displayName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "Common name for the roll of filament.",
            "nullable": true
          },
          "brand": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "materialType": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "The material Type, ie PLA, PETG, ABS.",
            "nullable": true
          },
          "materialCategoryNickname": {
            "type": "string",
            "nullable": true
          },
          "materialDensityGramPerCubicCm": {
            "type": "number",
            "description": "The Density of the Material",
            "format": "double"
          },
          "colorName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "colorHex": {
            "maxLength": 6,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "diameterMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "source": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Filament_SourceMeasurement"
          },
          "initialNominalVolumeMl": {
            "type": "number",
            "description": "The initial volume of the material in millileters",
            "format": "double",
            "nullable": true
          },
          "initialNominalLengthM": {
            "type": "number",
            "description": "The initial length of the material in meters",
            "format": "double",
            "nullable": true
          },
          "initialTotalWeightMg": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "initialNominalWeightMg": {
            "type": "integer",
            "description": "The initial nominal weight of the filament in milligrams.",
            "format": "int64",
            "nullable": true
          },
          "spoolWeightMg": {
            "type": "integer",
            "description": "The weight of the spool in milligrams.",
            "format": "int64",
            "nullable": true
          },
          "tempRangeStart": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "tempRangeEnd": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "recommendedTemp": {
            "type": "number",
            "description": "The user's recommended temperature",
            "format": "double",
            "nullable": true
          },
          "recommendedBedTemp": {
            "type": "number",
            "description": "The user's recommended Bed temperature",
            "format": "double",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "purchaseDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "purchaseLocation": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "description": "The location (either URL, or physical location where the filament was purchased)",
            "nullable": true
          },
          "purchasePriceValue": {
            "maxLength": 256,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "purchasePriceCurrency": {
            "maxLength": 256,
            "minLength": 0,
            "type": "string",
            "description": "The Currency Marker (ie, USD)",
            "nullable": true
          },
          "purchaseNotes": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "description": "Any notes about the purchase price",
            "nullable": true
          },
          "storageLocation": {
            "maxLength": 256,
            "minLength": 0,
            "type": "string",
            "description": "Where is this filament stored?",
            "nullable": true
          },
          "notes": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "isFavorite": {
            "type": "boolean"
          },
          "initialLayerTimeS": {
            "type": "number",
            "description": "The initial layer cure time in seconds",
            "format": "double",
            "nullable": true
          },
          "layerTimeS": {
            "type": "number",
            "description": "The layer cure time in seconds (after the initial layers)",
            "format": "double",
            "nullable": true
          },
          "meltingTemperature": {
            "type": "number",
            "description": "Melting temperature of the material",
            "format": "double",
            "nullable": true
          },
          "inertGas": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "What inert gas should be used?",
            "nullable": true
          },
          "materialRefreshRatio": {
            "maximum": 1,
            "minimum": 0,
            "type": "number",
            "description": "The percentage of new powder when mixing with old powder.\r\n1.0 means always use new powder.",
            "format": "double",
            "nullable": true
          },
          "filamentAdjustments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentAdjustmentDto"
            },
            "nullable": true
          },
          "colorPattern": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.ColorPatternType"
          },
          "finishType": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentFinishType"
          },
          "colors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "effects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentEffect"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentAdjustmentDto": {
        "required": [
          "filamentId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "filamentId": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "$ref": "#/components/schemas/PrintLogApi.Models.FilamentAdjustment_SourceMeasurement"
          },
          "amountMg": {
            "type": "integer",
            "description": "An amount to adjust the weight of a filament.\r\n\n\r\n    Amounts are added to the weight of the filament: \r\n    <list type=\"bullet\"><item>Positive Adjustment Amounts mean the addition of filament to the roll.</item><item>Negative Adjustment Amounts mean the removal of filament of the roll.</item></list>",
            "format": "int64",
            "nullable": true
          },
          "lengthInM": {
            "type": "number",
            "description": "The length of filament used in meters.\r\n\n\r\n    Amounts are added to the weight of the filament: \r\n    <list type=\"bullet\"><item>Positive Adjustment Amounts mean the addition of filament to the roll.</item><item>Negative Adjustment Amounts mean the removal of filament of the roll.</item></list>",
            "format": "double",
            "nullable": true
          },
          "volumeMl": {
            "type": "number",
            "description": "The volume of filament used in milliliters\r\n\n\r\n    Amounts are added to the Volume of the filament: \r\n    <list type=\"bullet\"><item>Positive Adjustment Amounts mean the addition of filament to the roll.</item><item>Negative Adjustment Amounts mean the removal of filament of the roll.</item></list>",
            "format": "double",
            "nullable": true
          },
          "notes": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "updatedDate": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentBrandsDto": {
        "type": "object",
        "properties": {
          "brands": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "A list of filament brands",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentDetailDto": {
        "required": [
          "displayName",
          "id",
          "materialType"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "Common name for the roll of filament."
          },
          "brand": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "materialCategoryNickname": {
            "type": "string",
            "nullable": true
          },
          "materialType": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "The material Type, ie PLA, PETG, ABS."
          },
          "materialDensityGramPerCubicCm": {
            "type": "number",
            "description": "The Density of the Material",
            "format": "double"
          },
          "colorName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "colorHex": {
            "maxLength": 6,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "diameterMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "source": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Filament_SourceMeasurement"
          },
          "initialNominalVolumeMl": {
            "type": "number",
            "description": "The initial volume of the material in millileters",
            "format": "double",
            "nullable": true
          },
          "initialNominalLengthM": {
            "type": "number",
            "description": "The initial length of the material in meters",
            "format": "double",
            "nullable": true
          },
          "initialTotalWeightMg": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "initialNominalWeightMg": {
            "type": "integer",
            "description": "The initial nominal weight of the filament in milligrams.",
            "format": "int64",
            "nullable": true
          },
          "spoolWeightMg": {
            "type": "integer",
            "description": "The weight of the spool in milligrams.",
            "format": "int64",
            "nullable": true
          },
          "filamentRemaining": {
            "type": "integer",
            "description": "Server-computed remaining filament weight in milligrams\r\n(InitialNominalWeightMg minus print usage plus weight adjustments).\r\nNull when nominal weight is not set. Read-only: ignored on write.",
            "format": "int64",
            "nullable": true
          },
          "filamentLengthRemainingInM": {
            "type": "number",
            "description": "Server-computed remaining length in meters, converted from FilamentRemaining so the\r\nthree readouts always describe the same spool. Null when the nominal weight is not\r\ntracked, and null for materials with no diameter - resin and powder have no length.\r\nGet-only: a client-supplied value cannot reach the server.",
            "format": "double",
            "nullable": true,
            "readOnly": true
          },
          "filamentVolumeRemainingInMl": {
            "type": "number",
            "description": "Server-computed remaining volume in milliliters, converted from FilamentRemaining.\r\nDeliberately NOT accumulated from the per-usage VolumeMl columns: a usage row written\r\nwithout normalization carries milligrams and no volume, and summing the volume column\r\nthen silently under-counts the spool. Get-only: a client-supplied value cannot reach\r\nthe server.",
            "format": "double",
            "nullable": true,
            "readOnly": true
          },
          "printCount": {
            "type": "integer",
            "description": "Distinct prints that used this filament. Read-only: ignored on write.",
            "format": "int32"
          },
          "totalUsedMg": {
            "type": "integer",
            "description": "Total milligrams consumed by prints, actual where recorded and estimated\r\notherwise - the same rule FilamentRemaining subtracts. Read-only: ignored on write.",
            "format": "int64"
          },
          "tempRangeStart": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "tempRangeEnd": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "recommendedTemp": {
            "type": "number",
            "description": "The user's recommended temperature",
            "format": "double",
            "nullable": true
          },
          "recommendedBedTemp": {
            "type": "number",
            "description": "The user's recommended bed temperature",
            "format": "double",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "purchaseDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "purchaseLocation": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "description": "The location (either URL, or physical location where the filament was purchased)",
            "nullable": true
          },
          "purchasePriceValue": {
            "maxLength": 256,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "purchasePriceCurrency": {
            "maxLength": 256,
            "minLength": 0,
            "type": "string",
            "description": "The Currency Marker (ie, USD)",
            "nullable": true
          },
          "purchaseNotes": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "description": "Any notes about the purchase price",
            "nullable": true
          },
          "storageLocation": {
            "maxLength": 256,
            "minLength": 0,
            "type": "string",
            "description": "Where is this filament stored?",
            "nullable": true
          },
          "notes": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "isFavorite": {
            "type": "boolean"
          },
          "initialLayerTimeS": {
            "type": "number",
            "description": "The initial layer cure time in seconds",
            "format": "double",
            "nullable": true
          },
          "layerTimeS": {
            "type": "number",
            "description": "The layer cure time in seconds (after the initial layers)",
            "format": "double",
            "nullable": true
          },
          "meltingTemperature": {
            "type": "number",
            "description": "Melting temperature of the material",
            "format": "double",
            "nullable": true
          },
          "inertGas": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "What inert gas should be used?",
            "nullable": true
          },
          "materialRefreshRatio": {
            "maximum": 1,
            "minimum": 0,
            "type": "number",
            "description": "The percentage of new powder when mixing with old powder.\r\n1.0 means always use new powder.",
            "format": "double",
            "nullable": true
          },
          "filamentAdjustments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentAdjustmentDto"
            },
            "nullable": true
          },
          "colorPattern": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.ColorPatternType"
          },
          "finishType": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentFinishType"
          },
          "colors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "effects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentEffect"
            },
            "nullable": true
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentImageDto"
            },
            "description": "Response-only, populated by `FilamentService.HydrateDetailImageUrlsAsync`.\r\nThis DTO doubles as the PUT request body, so a caller can send anything here —\r\nnothing reads it inbound. `Filament` has no `Images` navigation, so\r\nAutoMapper has nowhere to map it to; if one is ever added, this needs an\r\nexplicit `Ignore()` on the `FilamentDetailDto -> Filament` map or it\r\nbecomes an injection path.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentImageDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "url": {
            "type": "string",
            "description": "Signed, inline, bucketed-expiry URL. Null if signing failed.",
            "nullable": true
          },
          "thumbnailUrl": {
            "type": "string",
            "description": "Thumbnail URL; falls back to PrintLogApi.Models.DTOs.Filament.FilamentImageDto.Url when no thumbnail exists.",
            "nullable": true
          },
          "isDefault": {
            "type": "boolean"
          },
          "displayOrder": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentPurchaseLocationsDto": {
        "type": "object",
        "properties": {
          "purchaseLocations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "A list of purchase locations",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentStorageLocationDto": {
        "type": "object",
        "properties": {
          "storageLocations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "A list of storage locations",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "Common name for the roll of filament.",
            "nullable": true
          },
          "brand": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "materialType": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "The material Type, ie PLA, PETG, ABS.",
            "nullable": true
          },
          "materialCategory": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.MaterialCategory.MaterialCategoryDto"
          },
          "materialDensityGramPerCubicCm": {
            "type": "number",
            "description": "The Density of the Material",
            "format": "double"
          },
          "colorName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "colorHex": {
            "maxLength": 6,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "recommendedTemp": {
            "type": "number",
            "description": "The user's recommended temperature",
            "format": "double",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "notes": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "filamentRemaining": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "filamentLengthRemainingInM": {
            "type": "number",
            "description": "Converted from FilamentRemaining rather than accumulated separately, so the list and\r\nthe detail page cannot disagree. See FilamentDetailDto for why the per-usage volume\r\nand length columns are not summed. Get-only: computed on read.",
            "format": "double",
            "nullable": true,
            "readOnly": true
          },
          "filamentVolumeRemainingInMl": {
            "type": "number",
            "format": "double",
            "nullable": true,
            "readOnly": true
          },
          "purchasePriceValue": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "description": "Any notes about the purchase price",
            "nullable": true
          },
          "initialNominalWeightMg": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "diameterMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "loadedInPrinter": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummaryWithoutCategory"
          },
          "storageLocation": {
            "type": "string",
            "nullable": true
          },
          "isFavorite": {
            "type": "boolean"
          },
          "colorPattern": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.ColorPatternType"
          },
          "finishType": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentFinishType"
          },
          "colors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "effects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentEffect"
            },
            "nullable": true
          },
          "defaultImageThumbnailUrl": {
            "type": "string",
            "description": "Signed thumbnail URL for the default image. Populated ONLY by FilamentService's\r\nexplicit hydration for the owner's filament list. Null everywhere else —\r\nincluding inside print responses, which are anonymous.\r\nNever populate this via AutoMapper.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Filament.FilamentSummaryForPrinterDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "Common name for the roll of filament.",
            "nullable": true
          },
          "brand": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "materialType": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "The material Type, ie PLA, PETG, ABS.",
            "nullable": true
          },
          "colorName": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "colorHex": {
            "maxLength": 6,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "colorPattern": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.ColorPatternType"
          },
          "colors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "finishType": {
            "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentFinishType"
          },
          "effects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Enums.FilamentEffect"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Minimal filament summary for printer display lists.\r\nExcludes expensive calculated fields (remaining weight/volume/length)\r\nto improve query performance."
      },
      "PrintLogApi.Models.DTOs.Filament.PrinterMaintenanceCategoriesDto": {
        "type": "object",
        "properties": {
          "categories": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "A list of categories",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.MaterialCategory.MaterialCategoryDto": {
        "type": "object",
        "properties": {
          "nickname": {
            "maxLength": 50,
            "minLength": 0,
            "type": "string",
            "description": "The nickname of the material category",
            "nullable": true
          },
          "name": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "The long form name of the category",
            "nullable": true
          },
          "description": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "A description of that category",
            "nullable": true
          },
          "hasDiameter": {
            "type": "boolean",
            "description": "Whether this material has a diameter"
          },
          "showNozzleTemperature": {
            "type": "boolean"
          },
          "showBedTemperature": {
            "type": "boolean"
          },
          "showMeltingTemperature": {
            "type": "boolean"
          },
          "showRecommendedInitialLayerTimeInSeconds": {
            "type": "boolean"
          },
          "showRecommendedLayerTimeInSeconds": {
            "type": "boolean"
          },
          "showMaterialRefreshRatio": {
            "type": "boolean",
            "description": "The percentage of new powder when mixing with old powder"
          },
          "showInertGas": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "The category of materials, such as filament, resin, powder, etc."
      },
      "PrintLogApi.Models.DTOs.Materials.MaterialTypeDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "acronym": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "materialCategoryNickname": {
            "type": "string",
            "nullable": true
          },
          "densityGramPerCubicCm": {
            "type": "number",
            "description": "The Density of the Material",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Notification.AchievementNotificationDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "nullable": true
          },
          "tier": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "summary": {
            "type": "boolean"
          },
          "count": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The typed form of an PrintLogApi.Models.NotificationType.Achievement notification's metadata. Either\r\na single grant (PrintLogApi.Models.DTOs.Notification.AchievementNotificationDto.Key and PrintLogApi.Models.DTOs.Notification.AchievementNotificationDto.Tier) or the launch summary\r\n(PrintLogApi.Models.DTOs.Notification.AchievementNotificationDto.Summary and PrintLogApi.Models.DTOs.Notification.AchievementNotificationDto.Count)."
      },
      "PrintLogApi.Models.DTOs.Notification.MarkNotificationsReadDto": {
        "required": [
          "notificationIds"
        ],
        "type": "object",
        "properties": {
          "notificationIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Notification.NotificationDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "$ref": "#/components/schemas/PrintLogApi.Models.NotificationType"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "isRead": {
            "type": "boolean"
          },
          "createdDate": {
            "type": "string",
            "description": "UTC, with an explicit offset. A bare DateTime serializes without a designator, and\r\nJavaScript reads a designator-less ISO string as local time — which silently shifted\r\nevery notification timestamp in the web and mobile clients by the viewer's offset.",
            "format": "date-time"
          },
          "readDate": {
            "type": "string",
            "description": "UTC, with an explicit offset, for the same reason as PrintLogApi.Models.DTOs.Notification.NotificationDetailDto.CreatedDate.",
            "format": "date-time",
            "nullable": true
          },
          "actionUrl": {
            "type": "string",
            "nullable": true
          },
          "printId": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "printTitle": {
            "type": "string",
            "nullable": true
          },
          "commentId": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "triggeredByUser": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
          },
          "metadata": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Notification.NotificationSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "$ref": "#/components/schemas/PrintLogApi.Models.NotificationType"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "isRead": {
            "type": "boolean"
          },
          "createdDate": {
            "type": "string",
            "description": "UTC, with an explicit offset. A bare DateTime serializes without a designator, and\r\nJavaScript reads a designator-less ISO string as local time — which silently shifted\r\nevery notification timestamp in the web and mobile clients by the viewer's offset.",
            "format": "date-time"
          },
          "actionUrl": {
            "type": "string",
            "nullable": true
          },
          "printId": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "printTitle": {
            "type": "string",
            "nullable": true
          },
          "triggeredByUser": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
          },
          "achievement": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.AchievementNotificationDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Notification.NotificationUnreadCountDto": {
        "type": "object",
        "properties": {
          "unreadCount": {
            "type": "integer",
            "format": "int32"
          },
          "unreadAchievementCount": {
            "type": "integer",
            "description": "How many of the unread notifications are achievements. The web client's existing poll\r\nreads this to know when there is a celebration to fetch, without a second request.",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookExtraDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "path": {
            "type": "string",
            "nullable": true
          },
          "time": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookFilamentUsageDto": {
        "type": "object",
        "properties": {
          "length": {
            "type": "number",
            "format": "double"
          },
          "volumn": {
            "type": "number",
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookJobDto": {
        "type": "object",
        "properties": {
          "file": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookJobFileDto"
          },
          "estimatedPrintTime": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "averagePrintTime": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "lastPrintTime": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "filament": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookMetaAnalysisFilamentDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookJobFileDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookMetaAnalysisDto": {
        "type": "object",
        "properties": {
          "filament": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookMetaAnalysisFilamentDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookMetaAnalysisFilamentDto": {
        "type": "object",
        "properties": {
          "tool0": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookFilamentUsageDto"
          },
          "tool1": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookFilamentUsageDto"
          },
          "tool2": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookFilamentUsageDto"
          },
          "tool3": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookFilamentUsageDto"
          },
          "tool4": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookFilamentUsageDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookMetaDto": {
        "type": "object",
        "properties": {
          "hash": {
            "type": "string",
            "description": "SHA1 Hash of the file.",
            "nullable": true
          },
          "analysis": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Octoprint.OctoprintWebhookMetaAnalysisDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.AddPrintDTO": {
        "required": [
          "title"
        ],
        "type": "object",
        "properties": {
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "estimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "estimatedFilamentUsageMg": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "printTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "filamentUsageMg": {
            "type": "integer",
            "description": "Filament usage in milligrams",
            "format": "int32",
            "nullable": true
          },
          "filamentType": {
            "maxLength": 100,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "filamentUsage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintFilamentSummaryDto"
            },
            "nullable": true
          },
          "notes": {
            "maxLength": 50000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "url": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "title": {
            "maxLength": 100,
            "minLength": 0,
            "type": "string"
          },
          "fileName": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintViewStatus"
          },
          "allowComments": {
            "type": "boolean"
          },
          "projectId": {
            "type": "string",
            "description": "Assign to an existing project. Takes precedence over NewProjectName.",
            "format": "uuid",
            "nullable": true
          },
          "newProjectName": {
            "maxLength": 100,
            "type": "string",
            "description": "Create a new project inline and assign this print to it. Ignored if ProjectId is set.",
            "nullable": true
          },
          "curaSettingId": {
            "type": "string",
            "description": "The `CuraSetting` this print was prefilled from, when it came through the slicer\r\nplugin. Only provenance: the slicer name and version are copied onto the print. An id that\r\nis missing (purged) or owned by someone else is ignored, never rejected.",
            "format": "uuid",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.BulkDeletePrintsDto": {
        "type": "object",
        "properties": {
          "printIds": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            },
            "description": "The prints to delete. Must be non-empty, distinct, and at most 200 entries.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Permanently deletes many prints in one request, along with their comments, images,\r\nattachments, filament usage, and notifications."
      },
      "PrintLogApi.Models.DTOs.Print.BulkPrintFailureDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "reason": {
            "type": "string",
            "description": "Either \"NotFound\" or \"Forbidden\".",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One id that was not acted on. PrintLogApi.Models.DTOs.Print.BulkPrintFailureDto.Reason is a plain string, not a serialized\r\nenum, so it stays readable without depending on JSON converter configuration."
      },
      "PrintLogApi.Models.DTOs.Print.BulkPrintResultDto": {
        "type": "object",
        "properties": {
          "succeeded": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            },
            "nullable": true
          },
          "failed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.BulkPrintFailureDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The per-id outcome of a bulk operation. A bulk request is a 200 even when some ids\r\ncould not be acted on; the body is what says which."
      },
      "PrintLogApi.Models.DTOs.Print.BulkUpdatePrintsDto": {
        "type": "object",
        "properties": {
          "printIds": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            },
            "description": "The prints to update. Must be non-empty, distinct, and at most 200 entries.",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
          },
          "projectId": {
            "type": "string",
            "description": "Assign to an existing project. The project must belong to the caller.",
            "format": "uuid",
            "nullable": true
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintViewStatus"
          },
          "printerId": {
            "type": "integer",
            "description": "Reassign to one of the caller's printers.",
            "format": "int64",
            "nullable": true
          },
          "allowComments": {
            "type": "boolean",
            "nullable": true
          },
          "allowFileDownloads": {
            "type": "boolean",
            "nullable": true
          },
          "clear": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Field names to reset to null. Only \"projectId\" is clearable. A field may not be both\r\nset and cleared in one request.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Applies the same set of field values to many prints in one request. Every field is\r\noptional; omitted fields are left untouched. Enum values travel as their integer value,\r\nmatching the rest of the API."
      },
      "PrintLogApi.Models.DTOs.Print.ConfirmUploadRequest": {
        "required": [
          "blobPath",
          "contentType",
          "fileName"
        ],
        "type": "object",
        "properties": {
          "blobPath": {
            "minLength": 1,
            "type": "string"
          },
          "fileName": {
            "maxLength": 255,
            "minLength": 1,
            "type": "string"
          },
          "sizeBytes": {
            "maximum": 209715200,
            "minimum": 1,
            "type": "integer",
            "format": "int64"
          },
          "contentType": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.GetDownloadUrlResponse": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.GetUploadUrlRequest": {
        "required": [
          "contentType",
          "fileName"
        ],
        "type": "object",
        "properties": {
          "fileName": {
            "maxLength": 255,
            "minLength": 1,
            "type": "string"
          },
          "contentType": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "sizeBytes": {
            "maximum": 209715200,
            "minimum": 1,
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.GetUploadUrlResponse": {
        "type": "object",
        "properties": {
          "sasUrl": {
            "type": "string",
            "nullable": true
          },
          "blobPath": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.GroupedFeedItemDto": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "\"project\" or \"print\"",
            "nullable": true
          },
          "sortDate": {
            "type": "string",
            "description": "Used for chronological sort across both types.",
            "format": "date-time"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "projectStartDate": {
            "type": "string",
            "description": "The project's RESOLVED start date as a civil date. Populated for project rows only.",
            "format": "date",
            "nullable": true
          },
          "projectName": {
            "type": "string",
            "nullable": true
          },
          "projectReference": {
            "type": "string",
            "nullable": true
          },
          "projectStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectStatus"
          },
          "printCount": {
            "type": "integer",
            "description": "Total prints in this project (unfiltered).",
            "format": "int32",
            "nullable": true
          },
          "filteredPrintCount": {
            "type": "integer",
            "description": "Prints in this project matching the current filters.\r\nNull when no filters are active (all prints match).",
            "format": "int32",
            "nullable": true
          },
          "totalPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "totalEstimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "totalFilamentWeightMg": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "defaultProjectImageId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "filamentUsage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintFilamentSummaryDto"
            },
            "description": "Aggregated filament usage across all prints in this project,\r\ngrouped by FilamentId with weights summed.",
            "nullable": true
          },
          "printers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummary"
            },
            "description": "Distinct printers used across all prints in this project.",
            "nullable": true
          },
          "print": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintSummaryDTO"
          }
        },
        "additionalProperties": false,
        "description": "A single item in the grouped/interleaved print feed.\r\nType discriminator: \"project\" or \"print\"."
      },
      "PrintLogApi.Models.DTOs.Print.ImageOrderDto": {
        "type": "object",
        "properties": {
          "imageId": {
            "maximum": 2147483647,
            "minimum": 1,
            "type": "integer",
            "format": "int32"
          },
          "displayOrder": {
            "maximum": 2147483647,
            "minimum": 0,
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.PrintAttachmentDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "originalFileName": {
            "type": "string",
            "nullable": true
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          },
          "contentType": {
            "type": "string",
            "nullable": true
          },
          "displayOrder": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.PrintDetailDTO": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "printer": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummary"
          },
          "estimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "estimatedFilamentUsageMg": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "printTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "filamentUsageMg": {
            "type": "integer",
            "description": "Filament usage in milligrams",
            "format": "int32",
            "nullable": true
          },
          "filamentType": {
            "type": "string",
            "nullable": true
          },
          "filamentUsage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintFilamentSummaryDto"
            },
            "nullable": true
          },
          "notes": {
            "maxLength": 50000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string",
            "nullable": true
          },
          "fileName": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "createdByUserId": {
            "type": "integer",
            "format": "int64"
          },
          "allowComments": {
            "type": "boolean"
          },
          "allowFileDownloads": {
            "type": "boolean"
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintViewStatus"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintImageDto"
            },
            "nullable": true
          },
          "comments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Comments.CommentDetailDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.PrintFeedSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "createdBy": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.User.UserSummaryDto"
          },
          "printer": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummary"
          },
          "startDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
          },
          "estimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "printTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "defaultPrintImageId": {
            "type": "integer",
            "format": "int32"
          },
          "commentCount": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.PrintFilamentSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "filament": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentSummaryDto"
          },
          "estimatedAmountMg": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "amountMg": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "estimatedLengthInM": {
            "type": "number",
            "description": "The estimated length of filament used in meters.",
            "format": "double",
            "nullable": true
          },
          "lengthInM": {
            "type": "number",
            "description": "The actual length of filament used in meters.",
            "format": "double",
            "nullable": true
          },
          "estimatedVolumeMl": {
            "type": "number",
            "description": "The estimated length of filament used in meters.",
            "format": "double",
            "nullable": true
          },
          "volumeMl": {
            "type": "number",
            "description": "The actual volume of filament used in milliliters",
            "format": "double",
            "nullable": true
          },
          "isEstimatedLengthSource": {
            "type": "boolean",
            "description": "Determines if the user entered the length as the \"source of truth\", or weight. \r\nTrue means the user entered the filament usage as a length, while false means the user entered usage as Mg.",
            "deprecated": true
          },
          "isActualLengthSource": {
            "type": "boolean",
            "description": "Determines if the user entered the length as the \"source of truth\", or weight. \r\nTrue means the user entered the filament usage as a length, while false means the user entered usage as Mg.",
            "deprecated": true
          },
          "estimatedSource": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PrintFilament_SourceMeasurement"
          },
          "source": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PrintFilament_SourceMeasurement"
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.PrintImageDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "isDefault": {
            "type": "boolean"
          },
          "displayOrder": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.PrintStatistic": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "printerID": {
            "type": "integer",
            "format": "int64"
          },
          "startDate": {
            "type": "string",
            "description": "Nullable, because Print.StartDate is. Declared non-nullable, an undated print\r\nserialized as 0001-01-01T00:00:00+00:00, which every consumer reads as a real date in\r\nyear 1: it sorts first, falls outside every range filter, and renders as a garbage\r\ndate. Handle the null case explicitly — never with .Value or ?? default.",
            "format": "date-time",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
          },
          "estimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "estimatedFilamentUsageMg": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "printTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "filamentUsageMg": {
            "type": "integer",
            "description": "Filament usage in milligrams",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Used to get large collection of prints to run statistics from."
      },
      "PrintLogApi.Models.DTOs.Print.PrintSummaryDTO": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "printer": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummary"
          },
          "startDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
          },
          "estimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "printTimeInSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "filamentUsage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintFilamentSummaryDto"
            },
            "nullable": true
          },
          "sumActualFilamentWeightMg": {
            "type": "integer",
            "format": "int32"
          },
          "sumEstimatedFilamentWeightMg": {
            "type": "integer",
            "format": "int32"
          },
          "totalFilamentWeightMg": {
            "type": "integer",
            "format": "int32"
          },
          "createdByUserId": {
            "type": "integer",
            "format": "int64"
          },
          "defaultPrintImageId": {
            "type": "integer",
            "format": "int32"
          },
          "commentCount": {
            "type": "integer",
            "format": "int32"
          },
          "projectId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "projectName": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.PutPrintDetailDto": {
        "required": [
          "title"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The Print's ID",
            "format": "int64"
          },
          "title": {
            "maxLength": 100,
            "minLength": 0,
            "type": "string",
            "description": "The title of the print."
          },
          "startDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "printerId": {
            "type": "integer",
            "description": "The printer id of the printer this print was printed on.",
            "format": "int64"
          },
          "estimatedPrintTimeInSeconds": {
            "type": "integer",
            "description": "The estimated print duration in seconds.",
            "format": "int32",
            "nullable": true
          },
          "estimatedFilamentUsageMg": {
            "type": "integer",
            "description": "The non-tracked estimated Filament usage in milligrams. Used before the FilamentUsage collection was added.",
            "format": "int32",
            "nullable": true,
            "deprecated": true
          },
          "printTimeInSeconds": {
            "type": "integer",
            "description": "The actual print duration in seconds.",
            "format": "int32",
            "nullable": true
          },
          "filamentUsageMg": {
            "type": "integer",
            "description": "The non-tracked Filament usage in milligrams. Used before the FilamentUsage collection was added.",
            "format": "int32",
            "nullable": true,
            "deprecated": true
          },
          "filamentType": {
            "type": "string",
            "description": "The non-tracked Filament Type. Used before the FilamentUsage collection was added.",
            "nullable": true,
            "deprecated": true
          },
          "filamentUsage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PutPrintFilamentSummaryDto"
            },
            "description": "The collection of Filament Usage information.",
            "nullable": true
          },
          "notes": {
            "maxLength": 50000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "url": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "fileName": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "allowComments": {
            "type": "boolean",
            "description": "Whether or not comments are allowed on this print."
          },
          "allowFileDownloads": {
            "type": "boolean",
            "description": "Whether or not file downloads are allowed on this print."
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintStatus"
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Print_PrintViewStatus"
          },
          "projectId": {
            "type": "string",
            "description": "Assign to an existing project. Takes precedence over NewProjectName.",
            "format": "uuid",
            "nullable": true
          },
          "newProjectName": {
            "maxLength": 100,
            "type": "string",
            "description": "Create a new project inline and assign this print to it. Ignored if ProjectId is set.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "DTO for updating a print's detailed information."
      },
      "PrintLogApi.Models.DTOs.Print.PutPrintFilamentSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The GUID of the PrintFilament collection. Use EMPTY_GUID for new entries.",
            "format": "uuid"
          },
          "filamentId": {
            "type": "string",
            "description": "The optional GUID of the filament used. Set as null (or EMPTY_GUID) to not \r\nlink this usage to a filament, and instead treat this as a non-tracked filament.",
            "format": "uuid",
            "nullable": true
          },
          "estimatedAmountMg": {
            "type": "integer",
            "description": "The estimated weight of filament used in milligrams.",
            "format": "int32",
            "nullable": true
          },
          "amountMg": {
            "type": "integer",
            "description": "The actual weight of filament used in milligrams.",
            "format": "int32",
            "nullable": true
          },
          "estimatedLengthInM": {
            "type": "number",
            "description": "The estimated length of filament used in meters.",
            "format": "double",
            "nullable": true
          },
          "lengthInM": {
            "type": "number",
            "description": "The actual length of filament used in meters.",
            "format": "double",
            "nullable": true
          },
          "estimatedVolumeMl": {
            "type": "number",
            "description": "The estimated length of filament used in meters.",
            "format": "double",
            "nullable": true
          },
          "volumeMl": {
            "type": "number",
            "description": "The actual volume of filament used in milliliters",
            "format": "double",
            "nullable": true
          },
          "isEstimatedLengthSource": {
            "type": "boolean",
            "description": "Determines if the user entered the length as the \"source of truth\", or weight. \r\nTrue means the user entered the filament usage as a length, while false means the user entered usage as Mg.",
            "deprecated": true
          },
          "isActualLengthSource": {
            "type": "boolean",
            "description": "Determines if the user entered the length as the \"source of truth\", or weight. \r\nTrue means the user entered the filament usage as a length, while false means the user entered usage as Mg.",
            "deprecated": true
          },
          "estimatedSource": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PrintFilament_SourceMeasurement"
          },
          "source": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PrintFilament_SourceMeasurement"
          },
          "notes": {
            "type": "string",
            "description": "Any notes for this filament usage.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.ReorderImagesDto": {
        "required": [
          "images"
        ],
        "type": "object",
        "properties": {
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.ImageOrderDto"
            }
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Print.SinglePrintStat": {
        "type": "object",
        "properties": {
          "stat": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Used to encapsulate a single statistic, be it total print time, filament usage, etc."
      },
      "PrintLogApi.Models.DTOs.Printer.AddPrinterDTO": {
        "required": [
          "make",
          "model",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "make": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string"
          },
          "model": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string"
          },
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "description": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "nozzleDiameter": {
            "minimum": 0,
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "filamentDiameter": {
            "minimum": 0,
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "beamDiameter": {
            "minimum": 0,
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "loadedFilaments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.AddPrinterFilamentDto"
            },
            "description": "The collection of currently loaded filament for this printer.",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "category": {
            "type": "string",
            "description": "Nickname of the Printer Category for this printer (ie, FDM, FFF, etc)",
            "nullable": true
          },
          "bedWidthMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "bedHeightMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "bedDepthMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "screenResolutionXPixels": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "screenResolutionYPixels": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "hasHeatedBed": {
            "type": "boolean",
            "nullable": true
          },
          "hasHeatedChamber": {
            "type": "boolean",
            "nullable": true
          },
          "wattageW": {
            "minimum": 0,
            "type": "number",
            "format": "double",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Printer.AddPrinterFilamentDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The GUID of the PrintFilament collection. Use EMPTY_GUID for new entries.",
            "format": "uuid"
          },
          "filamentId": {
            "type": "string",
            "description": "The optional GUID of the filament used. Set as null (or EMPTY_GUID) to not \r\nlink this usage to a filament, and instead treat this as a non-tracked filament.",
            "format": "uuid"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "make": {
            "maxLength": 50,
            "type": "string",
            "nullable": true
          },
          "model": {
            "maxLength": 50,
            "type": "string",
            "nullable": true
          },
          "name": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "description": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "nozzleDiameter": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "filamentDiameter": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "loadedFilaments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterFilamentSummaryDto"
            },
            "description": "The collection of currently loaded filament for this printer.",
            "nullable": true
          },
          "category": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterCategory.PrinterCategoryDto"
          },
          "bedWidthMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "bedHeightMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "bedDepthMm": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "screenResolutionXPixels": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "screenResolutionYPixels": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "hasHeatedBed": {
            "type": "boolean",
            "nullable": true
          },
          "hasHeatedChamber": {
            "type": "boolean",
            "nullable": true
          },
          "wattageW": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterImageDto"
            },
            "description": "Photos of this printer, ordered by display order. Hydrated explicitly after\r\nmaterialization and never projected: SAS signing cannot run inside ProjectTo.",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterFilamentForSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "filament": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentSummaryForPrinterDto"
          }
        },
        "additionalProperties": false,
        "description": "Lightweight printer-filament relationship for summary views.\r\nUses FilamentSummaryForPrinterDto to avoid expensive calculations."
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterFilamentSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "filament": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentSummaryDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterImageDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "url": {
            "type": "string",
            "description": "Signed, inline, bucketed-expiry URL. Null if signing failed.",
            "nullable": true
          },
          "thumbnailUrl": {
            "type": "string",
            "description": "Thumbnail URL; falls back to PrintLogApi.Models.DTOs.Printer.PrinterImageDto.Url when no thumbnail exists.",
            "nullable": true
          },
          "isDefault": {
            "type": "boolean"
          },
          "displayOrder": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "make": {
            "type": "string",
            "nullable": true
          },
          "model": {
            "type": "string",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "wattageW": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "printTimeInSeconds": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "category": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterCategory.PrinterCategoryDto"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterSummarySimpleDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "make": {
            "type": "string",
            "nullable": true
          },
          "model": {
            "type": "string",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "wattageW": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "category": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterCategory.PrinterCategoryDto"
          },
          "loadedFilaments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterFilamentForSummaryDto"
            },
            "nullable": true
          },
          "defaultImageThumbnailUrl": {
            "type": "string",
            "description": "Signed thumbnail URL for this printer's default photo. Populated per-request, AFTER\r\nthe cache read - a signed URL cached for the entry's lifetime would outlive its\r\nsignature.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Simplified printer summary for list displays.\r\nUses lightweight filament DTOs to avoid expensive query calculations."
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterSummaryWithoutCategory": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "make": {
            "type": "string",
            "nullable": true
          },
          "model": {
            "type": "string",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Printer.PrinterThumbnailDto": {
        "type": "object",
        "properties": {
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "thumbnailUrl": {
            "type": "string",
            "description": "Signed thumbnail URL, or null when signing failed.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One entry in the caller's printer-thumbnail map."
      },
      "PrintLogApi.Models.DTOs.PrinterCategory.PrinterCategoryDto": {
        "type": "object",
        "properties": {
          "nickname": {
            "maxLength": 50,
            "minLength": 0,
            "type": "string",
            "description": "The nickname of the category",
            "nullable": true
          },
          "name": {
            "maxLength": 50,
            "minLength": 0,
            "type": "string",
            "description": "The long form name of the category",
            "nullable": true
          },
          "description": {
            "maxLength": 255,
            "minLength": 0,
            "type": "string",
            "description": "A description of that category",
            "nullable": true
          },
          "materialCategory": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.MaterialCategory.MaterialCategoryDto"
          },
          "showNozzleDiameter": {
            "type": "boolean"
          },
          "showFilamentDiameter": {
            "type": "boolean"
          },
          "showBeamDiameter": {
            "type": "boolean"
          },
          "showBedSize": {
            "type": "boolean"
          },
          "showScreenResolution": {
            "type": "boolean"
          },
          "showHasHeatedBed": {
            "type": "boolean"
          },
          "showHasHeatedChamber": {
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "The class/category/type of 3D printing technology, such as FDM, SLA, etc"
      },
      "PrintLogApi.Models.DTOs.PrinterMaintenance.AddPrinterMaintenanceDto": {
        "type": "object",
        "properties": {
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "done": {
            "type": "boolean"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "category": {
            "maxLength": 256,
            "type": "string",
            "nullable": true
          },
          "description": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "notes": {
            "maxLength": 1000,
            "type": "string",
            "description": "Additional Notes about the maintenance entry",
            "nullable": true
          },
          "priceValue": {
            "maxLength": 256,
            "type": "string",
            "description": "The value of the purchase price",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "printer": {
            "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Printer.PrinterSummary"
          },
          "done": {
            "type": "boolean"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "category": {
            "maxLength": 256,
            "type": "string",
            "nullable": true
          },
          "description": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "notes": {
            "maxLength": 1000,
            "type": "string",
            "description": "Additional Notes about the maintenance entry",
            "nullable": true
          },
          "priceValue": {
            "maxLength": 256,
            "type": "string",
            "description": "The value of the purchase price",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.PrinterMaintenance.PutPrinterMaintenanceDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "printerId": {
            "type": "integer",
            "format": "int64"
          },
          "done": {
            "type": "boolean"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "category": {
            "maxLength": 256,
            "type": "string",
            "nullable": true
          },
          "description": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "notes": {
            "maxLength": 1000,
            "type": "string",
            "description": "Additional Notes about the maintenance entry",
            "nullable": true
          },
          "priceValue": {
            "maxLength": 256,
            "type": "string",
            "description": "The value of the purchase price",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Project.AddProjectDto": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "reference": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "description": {
            "maxLength": 5000,
            "type": "string",
            "nullable": true
          },
          "url": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectStatus"
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectViewStatus"
          },
          "startDateOverride": {
            "type": "string",
            "description": "Manual start date, or null to derive the start date from the project's prints.\r\nThis is a full replace: omitting it clears an existing override.",
            "format": "date",
            "nullable": true
          },
          "finishDateOverride": {
            "type": "string",
            "description": "Manual finish date, or null to derive the finish date from the project's prints.\r\nThis is a full replace: omitting it clears an existing override.",
            "format": "date",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Project.ProjectDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectStatus"
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectViewStatus"
          },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "createdByUserId": {
            "type": "integer",
            "format": "int64"
          },
          "printCount": {
            "type": "integer",
            "format": "int32"
          },
          "totalPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "totalEstimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "totalFilamentWeightMg": {
            "type": "integer",
            "format": "int64"
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectImageDto"
            },
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "finishDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "startDateOverride": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "finishDateOverride": {
            "type": "string",
            "format": "date",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Project.ProjectImageDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "isDefault": {
            "type": "boolean"
          },
          "displayOrder": {
            "type": "integer",
            "format": "int32"
          },
          "url": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Project.ProjectSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectStatus"
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectViewStatus"
          },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "printCount": {
            "type": "integer",
            "format": "int32"
          },
          "totalPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "totalEstimatedPrintTimeInSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "totalFilamentWeightMg": {
            "type": "integer",
            "format": "int64"
          },
          "defaultImageId": {
            "type": "integer",
            "format": "int32"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "finishDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "startDateOverride": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "finishDateOverride": {
            "type": "string",
            "format": "date",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Project.PutProjectDto": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "reference": {
            "maxLength": 100,
            "type": "string",
            "nullable": true
          },
          "description": {
            "maxLength": 5000,
            "type": "string",
            "nullable": true
          },
          "url": {
            "maxLength": 1000,
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectStatus"
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.Project_ProjectViewStatus"
          },
          "startDateOverride": {
            "type": "string",
            "description": "Manual start date, or null to derive the start date from the project's prints.\r\nThis is a full replace: omitting it clears an existing override.",
            "format": "date",
            "nullable": true
          },
          "finishDateOverride": {
            "type": "string",
            "description": "Manual finish date, or null to derive the finish date from the project's prints.\r\nThis is a full replace: omitting it clears an existing override.",
            "format": "date",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Sitemap.SitemapEntryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The id the page URL is built from.",
            "format": "int64"
          },
          "lastModified": {
            "type": "string",
            "description": "The last time the row was saved, in UTC. Null when no trustworthy timestamp exists, so the\r\nsitemap omits `lastmod` rather than publishing an invented date.",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One public page for the UI's deploy-time sitemap generator: the entity id it builds the URL\r\nfrom, and when that entity was last modified."
      },
      "PrintLogApi.Models.DTOs.Subscription.CreateCheckoutSessionDto": {
        "required": [
          "planId"
        ],
        "type": "object",
        "properties": {
          "planId": {
            "minLength": 1,
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.Subscription.SubscriptionDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "nullable": true
          },
          "plan": {
            "type": "string",
            "nullable": true
          },
          "currentPeriodEnd": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancelAtPeriodEnd": {
            "type": "boolean"
          },
          "isPro": {
            "type": "boolean"
          },
          "maxImages": {
            "type": "integer",
            "description": "Images allowed per print, per filament and per printer. One cap governs all three.",
            "format": "int32"
          },
          "maxImagesPerPrint": {
            "type": "integer",
            "description": "Kept for wire compatibility with clients that predate PrintLogApi.Models.DTOs.Subscription.SubscriptionDto.MaxImages.\r\nAlways carries the same value.",
            "format": "int32"
          },
          "maxFilesPerPrint": {
            "type": "integer",
            "format": "int32"
          },
          "maxFileStorageBytes": {
            "type": "integer",
            "format": "int64"
          },
          "usedFileStorageBytes": {
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.User.UpdateUserDetailDto": {
        "type": "object",
        "properties": {
          "profilePicture": {
            "type": "string",
            "description": "URL pointing to the user's profile picture.",
            "nullable": true
          },
          "coverPicture": {
            "type": "string",
            "description": "URL pointing to the user's cover picture.",
            "nullable": true
          },
          "displayName": {
            "maxLength": 30,
            "minLength": 1,
            "type": "string",
            "nullable": true
          },
          "bio": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.User_ProfileViewStatus"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.User.UserSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "profilePicture": {
            "type": "string",
            "description": "URL pointing to the user's profile picture.",
            "nullable": true
          },
          "coverPicture": {
            "type": "string",
            "description": "URL pointing to the user's cover picture.",
            "nullable": true
          },
          "displayName": {
            "maxLength": 30,
            "minLength": 1,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.User.UserUrlDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Used to wrap new Urls in a json object."
      },
      "PrintLogApi.Models.DTOs.UserApiKeys.AddNewApiKeyDto": {
        "type": "object",
        "properties": {
          "description": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.UserApiKeys.NewUserApiKeyDto": {
        "type": "object",
        "properties": {
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "createdById": {
            "type": "integer",
            "format": "int64"
          },
          "updatedDate": {
            "type": "string",
            "format": "date-time"
          },
          "updatedById": {
            "type": "integer",
            "format": "int64"
          },
          "id": {
            "type": "string",
            "description": "Not the API Key, just the primary key",
            "format": "uuid"
          },
          "description": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "publicKey": {
            "type": "string",
            "description": "The new api key that was generated. This is the only time the API can return the public key",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.UserApiKeys.UserApiKeyDto": {
        "type": "object",
        "properties": {
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "createdById": {
            "type": "integer",
            "format": "int64"
          },
          "updatedDate": {
            "type": "string",
            "format": "date-time"
          },
          "updatedById": {
            "type": "integer",
            "format": "int64"
          },
          "id": {
            "type": "string",
            "description": "Not the API Key, just the primary key",
            "format": "uuid"
          },
          "description": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "isDeleted": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.UserDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "profilePicture": {
            "type": "string",
            "description": "URL pointing to the user's profile picture.",
            "nullable": true
          },
          "coverPicture": {
            "type": "string",
            "description": "URL pointing to the user's cover picture.",
            "nullable": true
          },
          "displayName": {
            "maxLength": 30,
            "minLength": 1,
            "type": "string",
            "nullable": true
          },
          "bio": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string",
            "nullable": true
          },
          "deactivationDateTime": {
            "type": "string",
            "description": "If present, the datetime that the user started the deactivation process.",
            "format": "date-time",
            "nullable": true
          },
          "viewStatus": {
            "$ref": "#/components/schemas/PrintLogApi.Models.User_ProfileViewStatus"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.UserSetting.AddUserSettingDto": {
        "type": "object",
        "properties": {
          "userSettingTypeId": {
            "type": "integer",
            "format": "int32"
          },
          "value": {
            "maxLength": 250,
            "minLength": 0,
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.UserSetting.UpdateUserSettingDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "value": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DTOs.UserSetting.UserSettingDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "userId": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "userSettingTypeId": {
            "type": "integer",
            "format": "int32"
          },
          "value": {
            "type": "string",
            "nullable": true
          },
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "updatedDate": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.DevicePlatform": {
        "enum": [
          1,
          2
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.Feedback_FeedbackType": {
        "enum": [
          1,
          2,
          3,
          4
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.FilamentAdjustment_SourceMeasurement": {
        "enum": [
          1,
          2,
          3
        ],
        "type": "integer",
        "description": "Which field is the user-entered \"source\"",
        "format": "int32"
      },
      "PrintLogApi.Models.Filament_SourceMeasurement": {
        "enum": [
          1,
          2,
          3
        ],
        "type": "integer",
        "description": "Which field is the user-entered \"source\"",
        "format": "int32"
      },
      "PrintLogApi.Models.NotificationType": {
        "enum": [
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.PageInfo": {
        "type": "object",
        "properties": {
          "currentPage": {
            "type": "integer",
            "format": "int32"
          },
          "totalPages": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          },
          "pageSize": {
            "type": "integer",
            "format": "int32"
          },
          "totalCount": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Filament.FilamentSummaryDto": {
        "type": "object",
        "properties": {
          "paging": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PageInfo"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Filament.FilamentSummaryDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "\n\r\n  `[ImmutableObject(true)]` is here for HybridCache and nothing else. It is inert at\r\n             runtime; the attribute is how HybridCache is told it may hand the same instance to every\r\n             caller instead of storing a serialized copy in L1 and deserializing it on each hit.\r\n\nWithout it a cache <i>hit</i> would pay a full JSON deserialize. That is the cost #66\r\n             declined to spend on the serialize side, and it would land on the endpoints whose expensive\r\n             part — the SQL aggregation — the cache already skips.\r\n\n\r\n  <b>Be clear about what it does and does not claim.</b> This type is mutable — both\r\n             properties have setters and `Items` is a mutable `List<T>` — so the attribute\r\n             is a statement of convention, not a property the compiler enforces: nothing mutates a\r\n             `PagedList` it read out of the cache, and nothing may start.\r\n\nWhat it is not is a new risk. A cached `PagedList` was already shared by reference\r\n             across concurrent requests under plain `IMemoryCache`; the attribute keeps that exact\r\n             behaviour rather than introducing it. The only alternative is letting HybridCache serialize\r\n             and re-deserialize per hit, which is a real cost paid on every request to buy protection\r\n             against a mutation that does not happen. Treat anything returned from a cache lookup as\r\n             read-only, and if a caller ever genuinely needs to mutate one, copy it first."
      },
      "PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Notification.NotificationSummaryDto": {
        "type": "object",
        "properties": {
          "paging": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PageInfo"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Notification.NotificationSummaryDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "\n\r\n  `[ImmutableObject(true)]` is here for HybridCache and nothing else. It is inert at\r\n             runtime; the attribute is how HybridCache is told it may hand the same instance to every\r\n             caller instead of storing a serialized copy in L1 and deserializing it on each hit.\r\n\nWithout it a cache <i>hit</i> would pay a full JSON deserialize. That is the cost #66\r\n             declined to spend on the serialize side, and it would land on the endpoints whose expensive\r\n             part — the SQL aggregation — the cache already skips.\r\n\n\r\n  <b>Be clear about what it does and does not claim.</b> This type is mutable — both\r\n             properties have setters and `Items` is a mutable `List<T>` — so the attribute\r\n             is a statement of convention, not a property the compiler enforces: nothing mutates a\r\n             `PagedList` it read out of the cache, and nothing may start.\r\n\nWhat it is not is a new risk. A cached `PagedList` was already shared by reference\r\n             across concurrent requests under plain `IMemoryCache`; the attribute keeps that exact\r\n             behaviour rather than introducing it. The only alternative is letting HybridCache serialize\r\n             and re-deserialize per hit, which is a real cost paid on every request to buy protection\r\n             against a mutation that does not happen. Treat anything returned from a cache lookup as\r\n             read-only, and if a caller ever genuinely needs to mutate one, copy it first."
      },
      "PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.GroupedFeedItemDto": {
        "type": "object",
        "properties": {
          "paging": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PageInfo"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.GroupedFeedItemDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "\n\r\n  `[ImmutableObject(true)]` is here for HybridCache and nothing else. It is inert at\r\n             runtime; the attribute is how HybridCache is told it may hand the same instance to every\r\n             caller instead of storing a serialized copy in L1 and deserializing it on each hit.\r\n\nWithout it a cache <i>hit</i> would pay a full JSON deserialize. That is the cost #66\r\n             declined to spend on the serialize side, and it would land on the endpoints whose expensive\r\n             part — the SQL aggregation — the cache already skips.\r\n\n\r\n  <b>Be clear about what it does and does not claim.</b> This type is mutable — both\r\n             properties have setters and `Items` is a mutable `List<T>` — so the attribute\r\n             is a statement of convention, not a property the compiler enforces: nothing mutates a\r\n             `PagedList` it read out of the cache, and nothing may start.\r\n\nWhat it is not is a new risk. A cached `PagedList` was already shared by reference\r\n             across concurrent requests under plain `IMemoryCache`; the attribute keeps that exact\r\n             behaviour rather than introducing it. The only alternative is letting HybridCache serialize\r\n             and re-deserialize per hit, which is a real cost paid on every request to buy protection\r\n             against a mutation that does not happen. Treat anything returned from a cache lookup as\r\n             read-only, and if a caller ever genuinely needs to mutate one, copy it first."
      },
      "PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Print.PrintSummaryDTO": {
        "type": "object",
        "properties": {
          "paging": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PageInfo"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Print.PrintSummaryDTO"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "\n\r\n  `[ImmutableObject(true)]` is here for HybridCache and nothing else. It is inert at\r\n             runtime; the attribute is how HybridCache is told it may hand the same instance to every\r\n             caller instead of storing a serialized copy in L1 and deserializing it on each hit.\r\n\nWithout it a cache <i>hit</i> would pay a full JSON deserialize. That is the cost #66\r\n             declined to spend on the serialize side, and it would land on the endpoints whose expensive\r\n             part — the SQL aggregation — the cache already skips.\r\n\n\r\n  <b>Be clear about what it does and does not claim.</b> This type is mutable — both\r\n             properties have setters and `Items` is a mutable `List<T>` — so the attribute\r\n             is a statement of convention, not a property the compiler enforces: nothing mutates a\r\n             `PagedList` it read out of the cache, and nothing may start.\r\n\nWhat it is not is a new risk. A cached `PagedList` was already shared by reference\r\n             across concurrent requests under plain `IMemoryCache`; the attribute keeps that exact\r\n             behaviour rather than introducing it. The only alternative is letting HybridCache serialize\r\n             and re-deserialize per hit, which is a real cost paid on every request to buy protection\r\n             against a mutation that does not happen. Treat anything returned from a cache lookup as\r\n             read-only, and if a caller ever genuinely needs to mutate one, copy it first."
      },
      "PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto": {
        "type": "object",
        "properties": {
          "paging": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PageInfo"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.PrinterMaintenance.PrinterMaintenanceDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "\n\r\n  `[ImmutableObject(true)]` is here for HybridCache and nothing else. It is inert at\r\n             runtime; the attribute is how HybridCache is told it may hand the same instance to every\r\n             caller instead of storing a serialized copy in L1 and deserializing it on each hit.\r\n\nWithout it a cache <i>hit</i> would pay a full JSON deserialize. That is the cost #66\r\n             declined to spend on the serialize side, and it would land on the endpoints whose expensive\r\n             part — the SQL aggregation — the cache already skips.\r\n\n\r\n  <b>Be clear about what it does and does not claim.</b> This type is mutable — both\r\n             properties have setters and `Items` is a mutable `List<T>` — so the attribute\r\n             is a statement of convention, not a property the compiler enforces: nothing mutates a\r\n             `PagedList` it read out of the cache, and nothing may start.\r\n\nWhat it is not is a new risk. A cached `PagedList` was already shared by reference\r\n             across concurrent requests under plain `IMemoryCache`; the attribute keeps that exact\r\n             behaviour rather than introducing it. The only alternative is letting HybridCache serialize\r\n             and re-deserialize per hit, which is a real cost paid on every request to buy protection\r\n             against a mutation that does not happen. Treat anything returned from a cache lookup as\r\n             read-only, and if a caller ever genuinely needs to mutate one, copy it first."
      },
      "PrintLogApi.Models.PagedListOfPrintLogApi.Models.DTOs.Project.ProjectSummaryDto": {
        "type": "object",
        "properties": {
          "paging": {
            "$ref": "#/components/schemas/PrintLogApi.Models.PageInfo"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PrintLogApi.Models.DTOs.Project.ProjectSummaryDto"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "\n\r\n  `[ImmutableObject(true)]` is here for HybridCache and nothing else. It is inert at\r\n             runtime; the attribute is how HybridCache is told it may hand the same instance to every\r\n             caller instead of storing a serialized copy in L1 and deserializing it on each hit.\r\n\nWithout it a cache <i>hit</i> would pay a full JSON deserialize. That is the cost #66\r\n             declined to spend on the serialize side, and it would land on the endpoints whose expensive\r\n             part — the SQL aggregation — the cache already skips.\r\n\n\r\n  <b>Be clear about what it does and does not claim.</b> This type is mutable — both\r\n             properties have setters and `Items` is a mutable `List<T>` — so the attribute\r\n             is a statement of convention, not a property the compiler enforces: nothing mutates a\r\n             `PagedList` it read out of the cache, and nothing may start.\r\n\nWhat it is not is a new risk. A cached `PagedList` was already shared by reference\r\n             across concurrent requests under plain `IMemoryCache`; the attribute keeps that exact\r\n             behaviour rather than introducing it. The only alternative is letting HybridCache serialize\r\n             and re-deserialize per hit, which is a real cost paid on every request to buy protection\r\n             against a mutation that does not happen. Treat anything returned from a cache lookup as\r\n             read-only, and if a caller ever genuinely needs to mutate one, copy it first."
      },
      "PrintLogApi.Models.PrintFilament_SourceMeasurement": {
        "enum": [
          1,
          2,
          3
        ],
        "type": "integer",
        "description": "Which field is the user-entered \"source\"",
        "format": "int32"
      },
      "PrintLogApi.Models.Print_PrintStatus": {
        "enum": [
          1,
          2,
          3,
          4,
          5,
          6
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.Print_PrintViewStatus": {
        "enum": [
          1,
          2,
          3
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.Project_ProjectStatus": {
        "enum": [
          1,
          2,
          3,
          4
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.Project_ProjectViewStatus": {
        "enum": [
          1,
          2,
          3
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.SortDirection": {
        "enum": [
          1,
          2
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.SortEnums.FilamentSummarySortColumn": {
        "enum": [
          1,
          2,
          3,
          4,
          5,
          6
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.SortEnums.PrintSummarySortColumn": {
        "enum": [
          1,
          2,
          3
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.SortEnums.PrinterMaintenanceSortColumn": {
        "enum": [
          1,
          2
        ],
        "type": "integer",
        "format": "int32"
      },
      "PrintLogApi.Models.User_ProfileViewStatus": {
        "enum": [
          1,
          2,
          3,
          4
        ],
        "type": "integer",
        "description": "The View Access for the user's profile.",
        "format": "int32"
      },
      "PrintLogApi.Services.Analytics.HistogramBucket": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "nullable": true
          },
          "lowerSeconds": {
            "type": "integer",
            "format": "int32"
          },
          "upperSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "count": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A half-open [LowerSeconds, UpperSeconds) duration bucket. Null upper means unbounded."
      },
      "PrintLogApi.Services.Analytics.MatrixCell": {
        "type": "object",
        "properties": {
          "weekday": {
            "type": "integer",
            "format": "int32"
          },
          "hour": {
            "type": "integer",
            "format": "int32"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "Weekday is 0-6 with Sunday = 0, matching DayOfWeek. Hour is local, 0-23."
      },
      "PrintLogApi.Services.Analytics.ScatterBin": {
        "type": "object",
        "properties": {
          "estimated": {
            "type": "number",
            "format": "double"
          },
          "actual": {
            "type": "number",
            "format": "double"
          },
          "count": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A binned scatter point: the bin's centre plus how many samples landed in it."
      },
      "PrintLogApi.Services.Analytics.StreakSummary": {
        "type": "object",
        "properties": {
          "currentDays": {
            "type": "integer",
            "format": "int32"
          },
          "longestDays": {
            "type": "integer",
            "format": "int32"
          },
          "longestStart": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "longestEnd": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "busiestDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "busiestDateCount": {
            "type": "integer",
            "format": "int32"
          },
          "busiestWeekday": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "busiestWeekdayCount": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.0 through Auth0. Use the authorization code flow with PKCE, and pass `audience=https://3dprintlog.com/api` to the authorization endpoint, or the issued token is not one this API accepts. The scopes listed are the OpenID Connect ones; this API grants a token's user full access to their own data and checks no scope of its own.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://3dprintlog.auth0.com/authorize",
            "tokenUrl": "https://3dprintlog.auth0.com/oauth/token",
            "refreshUrl": "https://3dprintlog.auth0.com/oauth/token",
            "scopes": {
              "openid": "Sign in, and identify the user.",
              "profile": "Read the user's name and picture.",
              "email": "Read the user's email address.",
              "offline_access": "Issue a refresh token."
            }
          }
        }
      },
      "apikey": {
        "type": "apiKey",
        "description": "A personal API key, created at https://www.3dprintlog.com/api-keys.",
        "name": "X-Api-Key",
        "in": "header"
      },
      "apikeyQuery": {
        "type": "apiKey",
        "description": "The same personal API key, for clients that cannot set a header.",
        "name": "api_key",
        "in": "query"
      }
    }
  },
  "tags": [
    {
      "name": "Achievements",
      "description": "Achievements: the badge catalog, the signed-in user's collection, and the hint card."
    },
    {
      "name": "Analytics",
      "description": "Read-only analytics aggregates for the authenticated user.\r\n            \r\nThe tenant comes from the token and nothing else: there is deliberately no userId\r\nparameter, unlike /api/Prints/summary. Filter ids the caller does not own simply match\r\nnothing, so the endpoint cannot be used to probe whether another user's printer exists."
    },
    {
      "name": "Comments",
      "description": "Editing and removing existing comments. Comments cannot be created by themselves, they need to be created \r\nattached to another resource (ie, to add a comment on a Print, use the POST api/Prints/{printid}/comment)."
    },
    {
      "name": "ConnectedAgents",
      "description": "Lists and revokes the current user's connected AI agents (Auth0 grants for the MCP audience).\r\nUses normal app authentication — NOT the MCP scope."
    },
    {
      "name": "Cura",
      "description": "Operations around saving settings coming from the Cura 3D Print Log Uploader plugin."
    },
    {
      "name": "Devices",
      "description": "Registration of mobile devices for push notifications."
    },
    {
      "name": "Feedbacks",
      "description": "Manage feedback."
    },
    {
      "name": "Feed",
      "description": "Used to retrieve list of prints for a feed-like view. Not currently in use."
    },
    {
      "name": "Filaments",
      "description": "Manage a user's list of filaments"
    },
    {
      "name": "MaterialCategories",
      "description": "Manage the list of material categories"
    },
    {
      "name": "MaterialTypes",
      "description": "Manage the list of default material types."
    },
    {
      "name": "Moonraker",
      "description": "Handles moonraker integration"
    },
    {
      "name": "Notifications",
      "description": "Operations involving user notifications."
    },
    {
      "name": "Octoprint",
      "description": "Handles incoming Octoprint Webhooks."
    },
    {
      "name": "PrinterCategories",
      "description": "Manage the list of printer categories"
    },
    {
      "name": "PrinterMaintenance",
      "description": "Manage a user's printer maintenance entries"
    },
    {
      "name": "Printers",
      "description": "Manage a user's list of printers."
    },
    {
      "name": "Prints",
      "description": "Operations involving Prints, print filament, print images, and print comments."
    },
    {
      "name": "UserApiKeys",
      "description": "Managing API keys for a user."
    },
    {
      "name": "Users",
      "description": "Manage 3D Print Log Users."
    },
    {
      "name": "UserSettings",
      "description": "Manage the currently authenticated user's list of settings. User Settings are used throughout the application to\r\nstore preferences, last selected printers, etc."
    },
    {
      "name": "UsersPrints",
      "description": "Used to retrieve specific stats about a specific user."
    }
  ]
}