{ "openapi": "3.1.0", "info": { "title": "FastAPI", "version": "0.1.0" }, "paths": { "/inf": { "post": { "summary": "Submit Image For Processing [Auth Required]", "operationId": "Submit_Image_for_Processing__AUTH_REQUIRED__inf_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InferenceRequest" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageSubmissionResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/workflow/{workflow_id}/status": { "get": { "summary": "Get Workflow Status [Auth Required]", "description": "Get comprehensive workflow status by workflow ID.\n\nAccepts any workflow_id (parent, processing, or inference child)\nand returns status for all related workflows.\n\nAuthorization: User must own the workflow OR be a CFIA admin.\n\nReturns detailed status information including:\n- Workflow type (parent/processing/inference)\n- Associated image ID\n- Overall status (pending/in_progress/completed/failed)\n- Parent workflow status and progress\n- Processing workflow stages and timestamps\n- Inference workflow status and details\n- Authorization metadata\n\nUse cases:\n- Frontend receives workflow_id from POST /inf submission\n- Frontend polls this endpoint to check progress\n- Can query by any related workflow ID (parent or child)\n\nReturns:\n Dict with comprehensive workflow status across all related workflows", "operationId": "Get_Workflow_Status__AUTH_REQUIRED__workflow__workflow_id__status_get", "parameters": [ { "name": "workflow_id", "in": "path", "required": true, "schema": { "type": "string", "title": "Workflow Id" } } ], "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowStatusResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/workflow/{workflow_id}/results": { "get": { "summary": "Get Workflow Results [Auth Required]", "description": "Get inference results for a completed workflow.\n\nReturns formatted inference results including bounding boxes, classifications,\nand model metadata when the workflow has completed successfully.\n\nAuthorization: User must own the workflow OR be a CFIA admin.\n\nArgs:\n workflow_id: Parent workflow ID returned by POST /inf\n\nReturns:\n ApiInferenceResponse with:\n - filename: Image filename\n - imageId: Image UUID\n - inference_id: Annotation UUID (equals workflow_id)\n - boxes: List of detected objects with classifications\n - labelOccurrence: Count of each label\n - totalBoxes: Total number of detected boxes\n - models: Model metadata used for inference\n\nRaises:\n 404: If workflow not found or results not ready\n 403: If user not authorized to access workflow\n 500: If results retrieval fails\n\nUse cases:\n- Frontend polls /workflow/{id}/status until overall_status == \"completed\"\n- Frontend then calls this endpoint to get formatted results\n- Results are stored in Annotation table with id == parent workflow_id", "operationId": "Get_Workflow_Results__AUTH_REQUIRED__workflow__workflow_id__results_get", "parameters": [ { "name": "workflow_id", "in": "path", "required": true, "schema": { "type": "string", "title": "Workflow Id" } } ], "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiInferenceResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/health": { "get": { "summary": "Get Health Status [No Auth Required]", "operationId": "Get_Health_Status__NO_AUTH_REQUIRED__health_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } } } } }, "/version": { "get": { "summary": "Get Api Version [No Auth Required]", "operationId": "Get_API_Version__NO_AUTH_REQUIRED__version_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VersionResponse" } } } } } } }, "/ready": { "get": { "summary": "Get Readiness Status [No Auth Required]", "operationId": "Get_Readiness_Status__NO_AUTH_REQUIRED__ready_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } } } } }, "/get-user-id": { "post": { "summary": "Get User Id From Email [Auth Required]", "operationId": "Get_User_ID_from_email__AUTH_REQUIRED__get_user_id_post", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserIdResponse" } } } } } } }, "/pipelines": { "get": { "summary": "Get Pipelines [Auth Required]", "operationId": "Get_Pipelines__AUTH_REQUIRED__pipelines_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PipelinesResponse" } } } } } } }, "/model-endpoints-metadata": { "get": { "summary": "Get Model Endpoints Metadata [Auth Required]", "operationId": "Get_Model_Endpoints_Metadata__AUTH_REQUIRED__model_endpoints_metadata_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModelEndpointsMetadataResponse" } } } } } } }, "/seeds": { "get": { "summary": "Get Seed Data [Auth Required]", "operationId": "Get_Seed_Data__AUTH_REQUIRED__seeds_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeedDataResponse" } } } } } } }, "/devices": { "get": { "summary": "Get All Devices [Auth Required]", "operationId": "Get_All_Devices__AUTH_REQUIRED__devices_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DevicesResponse" } } } } } } }, "/get-directories": { "get": { "summary": "Get Directories [Auth Required]", "operationId": "Get_Directories__AUTH_REQUIRED__get_directories_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DirectoriesResponse" } } } } } } }, "/folders": { "post": { "summary": "Create Or Get Folder [Auth Required]", "description": "Get or create a folder using the get-or-create pattern (idempotent).\n\nThis endpoint checks if a folder exists at the given normalized_path.\nIf it exists, returns the existing folder_id. If not, creates a new folder\nand returns the new folder_id.\n\nAuthorization: Any user belonging to an organization\n\nRequest:\n normalized_path: Relative path (e.g., \"avena-fatua\" or \"mycology/avena-fatua\")\n description: Optional description for the folder (defaults to empty string)\n\nResponse:\n folder_id: UUID of the existing or newly created folder", "operationId": "Create_or_Get_Folder__AUTH_REQUIRED__folders_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateOrGetFolderRequest" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateFolderResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/folders/{folder_id}": { "put": { "summary": "Update Folder [Auth Required]", "description": "Update a folder's name and/or description.\n\nAuthorization: Users with folder's org_user_role_id OR org_admin_role_id OR CFIA admin\nRestrictions: Cannot update default folders for active users\n\nRequest:\n name: Optional new folder name (just the name, not the full path)\n description: Optional new description\n\nResponse:\n id: UUID of the updated folder\n message: Success message\n\nRaises:\n 400: If no fields provided or name conflicts with existing folder\n 403: If user lacks permission or folder is a default folder\n 404: If folder not found", "operationId": "Update_Folder__AUTH_REQUIRED__folders__folder_id__put", "parameters": [ { "name": "folder_id", "in": "path", "required": true, "schema": { "type": "string", "title": "Folder Id" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateFolderRequest" } } } }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateFolderResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } }, "delete": { "summary": "Delete Folder [Auth Required]", "description": "Delete (soft delete) a folder.\n\nAuthorization: Folder creator (user_id matches) OR org admin OR CFIA admin\nRestrictions: Cannot delete default folders for active users\n\nResponse:\n id: UUID of the deleted folder\n message: Success message\n\nRaises:\n 403: If user lacks permission or folder is a default folder for active user\n 404: If folder not found", "operationId": "Delete_Folder__AUTH_REQUIRED__folders__folder_id__delete", "parameters": [ { "name": "folder_id", "in": "path", "required": true, "schema": { "type": "string", "title": "Folder Id" } } ], "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeleteFolderResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/is-registered": { "get": { "summary": "Check If User Is Registered [Auth Required]", "operationId": "Check_if_User_is_Registered__AUTH_REQUIRED__is_registered_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegistrationStatusResponse" } } } } } } }, "/logout": { "get": { "summary": "Logout User [No Auth Required]", "operationId": "Logout_User__NO_AUTH_REQUIRED__logout_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": {} } } } } } }, "/logs": { "post": { "summary": "Frontend Log Endpoint [Auth Required]", "description": "Accept frontend log messages for centralized logging.\n\nNow uses FrontendLogRequest Pydantic model for validation:\n- Enforces max lengths on all fields\n- Removes control characters\n- Validates log level enum", "operationId": "Frontend_Log_Endpoint__AUTH_REQUIRED__logs_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FrontendLogRequest" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LogSubmissionResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/new-batch-import": { "post": { "summary": "Initialize Batch Upload [Auth Required]", "description": "Initialize batch upload session.\n\nCreates a database-backed session for batch uploading images to an existing folder.\nSessions expire after 24 hours and support up to 1000 files.\n\nSecurity:\n- Requires authentication (Bearer token)\n- Rate limited to 10 requests/minute\n- Validates folder exists and belongs to user\n\nRequest:\n - folder_id: UUID of existing folder (MUST exist before batch upload)\n - file_count: Number of images to upload (max 1000)\n\nResponse:\n - session_id: UUID for subsequent uploads (valid for 24 hours)\n\nWorkflow:\n1. Validate user has org roles\n2. Validate folder exists and belongs to user\n3. Validate file_count <= 1000\n4. Create database session with 24-hour TTL\n5. Return session_id\n\nConstraints:\n- Maximum 1000 files per session\n- Session expires after 24 hours\n- Folder must exist before session creation\n- Folder must belong to authenticated user", "operationId": "Initialize_Batch_Upload__AUTH_REQUIRED__new_batch_import_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchUploadInitRequest" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchUploadInitResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/upload-picture": { "post": { "summary": "Upload Picture In Batch [Auth Required]", "description": "Upload single picture in batch - ASYNC WORKFLOW with duplicate detection.\n\nReturns immediately with workflow_id. Frontend must poll\nGET /workflow/{workflow_id}/status for completion.\n\nWorkflow steps (background):\n1. Upload to EXTERNAL storage (nachet-original)\n2. Azure Defender malware scan\n3. Sanitization function\n4. Store in INTERNAL storage (nachet-sanitized)\n\nSecurity:\n- Requires authentication (Bearer token)\n- Rate limited to 60 requests/minute\n- Validates session ownership, expiration, and active status\n- Validates seed exists\n- Reuses existing DBOS workflow (Defender + sanitization)\n\nRequest:\n - session_id: From /new-batch-import (must be active and not expired)\n - seed_id: UUID of existing seed record (validated before upload)\n - tray_code: Sample tray identifier (A-E)\n - sample_id: Becomes picture.name field\n - device_*_id, magnification: Device metadata\n - image: Base64 data URL\n\nResponse (Success):\n - workflow_id: Poll /workflow/{id}/status\n - picture_id: Image UUID\n\nResponse (Duplicate):\n - 400 BAD_REQUEST with existing picture_id\n - Both uploaded_count and duplicate_count incremented\n - No workflow enqueued\n\nResponse (Session Expired):\n - 400 BAD_REQUEST \"Session expired (24-hour limit exceeded)\"\n\nResponse (Invalid Seed):\n - 400 BAD_REQUEST \"Seed not found: {seed_id}\"\n\nDuplicate Handling:\n- Detects duplicates via SHA256 hash collision\n- Increments session uploaded_count and duplicate_count\n- Returns error with existing picture_id\n- Does NOT create new picture or enqueue workflow\n\nSession Completion:\n- When uploaded_count >= file_count, session marked inactive\n- Both successful uploads and duplicates count toward file_count\n\nNote: FRONTEND MODIFICATION REQUIRED for async polling.\nSee BATCH_UPLOAD_IMPLEMENTATION_PLAN.md for details.", "operationId": "Upload_Picture_in_Batch__AUTH_REQUIRED__upload_picture_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchUploadImageRequest" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchUploadImageResponse" } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } } }, "components": { "schemas": { "ApiInferenceBox": { "properties": { "box": { "$ref": "#/components/schemas/PixelBoundingBox" }, "label": { "type": "string", "title": "Label", "description": "Primary classification label" }, "score": { "type": "number", "maximum": 1.0, "minimum": 0.0, "title": "Score", "description": "Confidence score" }, "topN": { "items": { "$ref": "#/components/schemas/PredictionLabelScore" }, "type": "array", "title": "Topn", "description": "Top-N predictions" }, "classId": { "type": "string", "title": "Classid", "description": "Unique identifier for this classification" }, "objectTypeId": { "type": "string", "title": "Objecttypeid", "description": "Type identifier for the detected object" }, "boxId": { "type": "string", "title": "Boxid", "description": "Unique identifier for this bounding box" }, "overlapping": { "type": "boolean", "title": "Overlapping", "description": "Whether this box overlaps with others" }, "overlappingIndices": { "type": "integer", "title": "Overlappingindices", "description": "Index of overlapping box (if any), -1 if none" }, "isVerified": { "type": "boolean", "title": "Isverified", "description": "Whether this box has been verified by user", "default": false } }, "type": "object", "required": [ "box", "label", "score", "topN", "classId", "objectTypeId", "boxId", "overlapping", "overlappingIndices" ], "title": "ApiInferenceBox", "description": "Inference box matching frontend InferenceBoxApiSchema.\n\nThis is the exact format expected by the frontend for each box in the inference results.\nIncludes all required fields for frontend visualization and interaction.\n\nNote: Frontend expects PIXEL coordinates, not normalized coordinates!" }, "ApiInferenceResponse": { "properties": { "filename": { "type": "string", "title": "Filename" }, "imageId": { "type": "string", "title": "Imageid", "description": "Image UUID or identifier" }, "inferenceId": { "type": "string", "title": "Inferenceid", "description": "Inference UUID or identifier" }, "boxes": { "items": { "$ref": "#/components/schemas/ApiInferenceBox" }, "type": "array", "title": "Boxes" }, "labelOccurrence": { "additionalProperties": { "type": "integer" }, "type": "object", "title": "Labeloccurrence", "description": "Count of each label found in the image" }, "totalBoxes": { "type": "integer", "title": "Totalboxes", "description": "Total number of detected boxes" }, "models": { "items": { "$ref": "#/components/schemas/ModelInfo" }, "type": "array", "title": "Models", "description": "Models used for inference" } }, "type": "object", "required": [ "filename", "imageId", "inferenceId", "boxes", "labelOccurrence", "totalBoxes", "models" ], "title": "ApiInferenceResponse", "description": "Complete inference response matching frontend ApiInferenceDataSchema.\n\nThis is the format expected by the frontend for inference results.\nUsed by /inf-direct endpoint." }, "BatchUploadImageRequest": { "properties": { "sessionId": { "type": "string", "title": "Sessionid", "description": "Session UUID from /new-batch-import" }, "seedId": { "type": "string", "title": "Seedid", "description": "UUID of existing seed record" }, "trayCode": { "type": "string", "enum": [ "A", "B", "C", "D", "E" ], "title": "Traycode" }, "sampleId": { "type": "string", "maxLength": 100, "minLength": 1, "title": "Sampleid", "description": "Becomes picture.name" }, "imageDescription": { "type": "string", "maxLength": 500, "title": "Imagedescription", "description": "Description for the image" }, "deviceBrandId": { "type": "string", "title": "Devicebrandid" }, "deviceModelId": { "type": "string", "title": "Devicemodelid" }, "deviceLensId": { "type": "string", "title": "Devicelensid" }, "magnification": { "type": "number", "exclusiveMaximum": 1000.0, "exclusiveMinimum": 0.1, "title": "Magnification" }, "image": { "type": "string", "title": "Image", "description": "Base64 data URL" } }, "type": "object", "required": [ "sessionId", "seedId", "trayCode", "sampleId", "imageDescription", "deviceBrandId", "deviceModelId", "deviceLensId", "magnification", "image" ], "title": "BatchUploadImageRequest", "description": "Upload single image in batch.\n\nThis request is submitted for each image in the batch session.\nThe image goes through the full security pipeline (Defender scan + sanitization).\nDuplicate images (same SHA256 hash) are tracked but not saved." }, "BatchUploadImageResponse": { "properties": { "success": { "type": "boolean", "title": "Success" }, "pictureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Pictureid" }, "workflowId": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Workflowid", "description": "For status polling" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" } }, "type": "object", "required": [ "success" ], "title": "BatchUploadImageResponse", "description": "Response from image upload - ASYNC WORKFLOW PATTERN.\n\nThe image is queued for processing in the background.\nFrontend must poll GET /workflow/{workflow_id}/status for completion." }, "BatchUploadInitRequest": { "properties": { "folderId": { "type": "string", "title": "Folderid", "description": "UUID of existing folder" }, "fileCount": { "type": "integer", "maximum": 1000.0, "exclusiveMinimum": 0.0, "title": "Filecount", "description": "Number of files to upload (max 1000)" } }, "type": "object", "required": [ "folderId", "fileCount" ], "title": "BatchUploadInitRequest", "description": "Initialize batch upload session.\n\nThe folder must exist before batch upload starts.\nReturns a session_id for subsequent upload requests.\nSessions expire after 24 hours and support up to 1000 files." }, "BatchUploadInitResponse": { "properties": { "sessionId": { "type": "string", "title": "Sessionid", "description": "UUID of created session" } }, "type": "object", "required": [ "sessionId" ], "title": "BatchUploadInitResponse", "description": "Response from batch initialization.\n\nSession expires after 24 hours and accepts up to file_count uploads." }, "BlobUrls": { "properties": { "original": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Original" }, "sanitized": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Sanitized" } }, "type": "object", "title": "BlobUrls", "description": "Blob storage URLs for original and sanitized images." }, "CreateFolderResponse": { "properties": { "folderId": { "type": "string", "title": "Folderid", "description": "UUID of existing or newly created folder" } }, "type": "object", "required": [ "folderId" ], "title": "CreateFolderResponse", "description": "Response model for folder creation (get-or-create pattern)." }, "CreateOrGetFolderRequest": { "properties": { "normalizedPath": { "type": "string", "title": "Normalizedpath" }, "description": { "type": "string", "title": "Description", "default": "" } }, "type": "object", "required": [ "normalizedPath" ], "title": "CreateOrGetFolderRequest", "description": "Request model for creating or retrieving a folder.\n\nThe normalized_path is a relative path (e.g., 'avena-fatua' or 'mycology/avena-fatua').\nThe backend will prepend the user's organization prefix automatically.\n\nValidation is performed in the service layer via DirectoryService._validate_and_parse_fullpath().\n\nThe description field is optional and defaults to empty string if not provided." }, "DefenderScanResult": { "properties": { "status": { "type": "string", "title": "Status" }, "tags": { "additionalProperties": true, "type": "object", "title": "Tags" }, "scanTimestamp": { "type": "string", "title": "Scantimestamp" } }, "type": "object", "required": [ "status", "tags", "scanTimestamp" ], "title": "DefenderScanResult", "description": "Microsoft Defender for Storage scan result." }, "DeleteFolderResponse": { "properties": { "id": { "type": "string", "title": "Id", "description": "UUID of the deleted folder" }, "message": { "type": "string", "title": "Message", "description": "Success message" } }, "type": "object", "required": [ "id", "message" ], "title": "DeleteFolderResponse", "description": "Response model for folder deletion (soft delete)." }, "DeviceBrand": { "properties": { "id": { "type": "string", "title": "Id", "description": "UUID of the device brand" }, "name": { "type": "string", "title": "Name", "description": "Brand name" }, "description": { "type": "string", "title": "Description", "description": "Brand description" }, "models": { "items": { "$ref": "#/components/schemas/DeviceModel" }, "type": "array", "title": "Models", "description": "List of models for this brand" }, "lenses": { "items": { "$ref": "#/components/schemas/DeviceLens" }, "type": "array", "title": "Lenses", "description": "List of lenses for this brand" } }, "type": "object", "required": [ "id", "name", "description", "models", "lenses" ], "title": "DeviceBrand", "description": "Device brand with associated models and lenses.\n\nRepresents a manufacturer brand with its available models and lenses." }, "DeviceData": { "properties": { "devices": { "items": { "$ref": "#/components/schemas/DeviceBrand" }, "type": "array", "title": "Devices", "description": "List of device brands" } }, "type": "object", "required": [ "devices" ], "title": "DeviceData", "description": "Wrapper for device data list." }, "DeviceLens": { "properties": { "id": { "type": "string", "title": "Id", "description": "UUID of the device lens" }, "name": { "type": "string", "title": "Name", "description": "Lens name" }, "description": { "type": "string", "title": "Description", "description": "Lens description" } }, "type": "object", "required": [ "id", "name", "description" ], "title": "DeviceLens", "description": "Device lens entry." }, "DeviceModel": { "properties": { "id": { "type": "string", "title": "Id", "description": "UUID of the device model" }, "name": { "type": "string", "title": "Name", "description": "Model name" }, "description": { "type": "string", "title": "Description", "description": "Model description" } }, "type": "object", "required": [ "id", "name", "description" ], "title": "DeviceModel", "description": "Device model entry." }, "DevicesResponse": { "$ref": "#/components/schemas/DeviceData", "title": "DevicesResponse", "description": "Response model for GET /devices endpoint.\n\nReturns device information (brands, models, lenses) for image metadata.\nUses RootModel wrapping DeviceData for proper camelCase serialization." }, "DirectoriesResponse": { "$ref": "#/components/schemas/DirectoryData", "title": "DirectoriesResponse", "description": "Response model for GET /get-directories endpoint.\n\nReturns user's directory/folder tree structure.\nUses RootModel wrapping DirectoryData for proper camelCase serialization." }, "DirectoryData": { "properties": { "directories": { "items": { "$ref": "#/components/schemas/DirectoryItem" }, "type": "array", "title": "Directories", "description": "List of user directories" } }, "type": "object", "required": [ "directories" ], "title": "DirectoryData", "description": "Wrapper for directory data list." }, "DirectoryItem": { "properties": { "id": { "type": "string", "title": "Id", "description": "UUID of the directory" }, "name": { "type": "string", "title": "Name", "description": "Directory name" }, "folderPrefix": { "type": "string", "title": "Folderprefix", "description": "Organization prefix for the folder" }, "description": { "type": "string", "title": "Description", "description": "Directory description" }, "pictureCount": { "type": "integer", "title": "Picturecount", "description": "Number of pictures in this directory" }, "isDefaultFolder": { "type": "boolean", "title": "Isdefaultfolder", "description": "Whether this is the user's default folder" } }, "type": "object", "required": [ "id", "name", "folderPrefix", "description", "pictureCount", "isDefaultFolder" ], "title": "DirectoryItem", "description": "Individual directory/folder entry.\n\nRepresents a user's folder with picture count and metadata." }, "FrontendLogRequest": { "properties": { "level": { "type": "string", "enum": [ "ERROR", "WARNING", "INFO" ], "title": "Level", "description": "Log level", "default": "ERROR" }, "message": { "type": "string", "maxLength": 1000, "title": "Message", "description": "Log message" }, "errorType": { "anyOf": [ { "type": "string", "maxLength": 200 }, { "type": "null" } ], "title": "Errortype", "description": "Error type or name" }, "stackTrace": { "anyOf": [ { "type": "string", "maxLength": 5000 }, { "type": "null" } ], "title": "Stacktrace", "description": "Stack trace for errors" }, "url": { "anyOf": [ { "type": "string", "maxLength": 500 }, { "type": "null" } ], "title": "Url", "description": "URL where error occurred" }, "correlationId": { "anyOf": [ { "type": "string", "maxLength": 100 }, { "type": "null" } ], "title": "Correlationid", "description": "Correlation ID for request tracing" }, "sessionId": { "anyOf": [ { "type": "string", "maxLength": 100 }, { "type": "null" } ], "title": "Sessionid", "description": "Frontend session ID" } }, "type": "object", "required": [ "message" ], "title": "FrontendLogRequest", "description": "Request model for frontend log submission.\n\nFrontend sends error logs to backend for centralized logging.\nAll fields are validated to prevent abuse (e.g., extremely long strings)." }, "HTTPValidationError": { "properties": { "detail": { "items": { "$ref": "#/components/schemas/ValidationError" }, "type": "array", "title": "Detail" } }, "type": "object", "title": "HTTPValidationError" }, "HealthResponse": { "properties": { "status": { "type": "string", "title": "Status", "description": "Health status (e.g., 'ok', 'ready')" } }, "type": "object", "required": [ "status" ], "title": "HealthResponse", "description": "Response model for GET /health and GET /ready endpoints.\n\nSimple health check response indicating the service is operational." }, "ImageSubmissionResponse": { "properties": { "imageId": { "type": "string", "title": "Imageid" }, "workflowId": { "type": "string", "title": "Workflowid" }, "status": { "type": "string", "title": "Status" }, "message": { "type": "string", "title": "Message" } }, "type": "object", "required": [ "imageId", "workflowId", "status", "message" ], "title": "ImageSubmissionResponse", "description": "Response model for image submission to processing pipeline." }, "InferenceRequest": { "properties": { "pipelineId": { "type": "string", "title": "Pipelineid" }, "folderName": { "type": "string", "title": "Foldername" }, "folderId": { "type": "string", "title": "Folderid" }, "imageDims": { "items": { "type": "integer" }, "type": "array", "maxItems": 2, "minItems": 2, "title": "Imagedims", "description": "Image dimensions as [width, height]" }, "image": { "type": "string", "title": "Image" }, "areaRatio": { "type": "number", "title": "Arearatio", "default": 0.5 }, "colorFormat": { "type": "string", "title": "Colorformat", "default": "hex" }, "imageName": { "type": "string", "maxLength": 100, "title": "Imagename", "description": "Human-readable name for the image" }, "imageDescription": { "type": "string", "maxLength": 500, "title": "Imagedescription", "description": "Description or notes about the image" }, "deviceModelId": { "type": "string", "format": "uuid", "title": "Devicemodelid", "description": "UUID of the device model used to capture the image" }, "deviceLensId": { "type": "string", "format": "uuid", "title": "Devicelensid", "description": "UUID of the lens used to capture the image" }, "trayCode": { "$ref": "#/components/schemas/TrayCode", "description": "Sample tray identification code (A-E only)" }, "magnification": { "type": "number", "title": "Magnification", "description": "Magnification level used when capturing the image" } }, "type": "object", "required": [ "pipelineId", "folderName", "folderId", "imageDims", "image", "imageName", "imageDescription", "deviceModelId", "deviceLensId", "trayCode", "magnification" ], "title": "InferenceRequest", "description": "Request model for POST /inf endpoint.\n\nMatches legacy API format for backwards compatibility.\nIncludes required image metadata fields sent by frontend." }, "InferenceRequestPayload": { "properties": { "pictureId": { "type": "string", "title": "Pictureid" }, "pipelineId": { "type": "string", "title": "Pipelineid" }, "imageDims": { "items": { "type": "integer" }, "type": "array", "title": "Imagedims" }, "workflowId": { "type": "string", "title": "Workflowid" } }, "type": "object", "required": [ "pictureId", "pipelineId", "imageDims", "workflowId" ], "title": "InferenceRequestPayload", "description": "Payload for inference workflow request." }, "InferenceWorkflowStatus": { "properties": { "workflowId": { "type": "string", "title": "Workflowid" }, "status": { "type": "string", "title": "Status" }, "pipelineId": { "type": "string", "title": "Pipelineid" }, "createdAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Createdat" }, "startedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Startedat" }, "completedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Completedat" }, "failedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Failedat" }, "errorMessage": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Errormessage" }, "requestPayload": { "anyOf": [ { "$ref": "#/components/schemas/InferenceRequestPayload" }, { "type": "null" } ] } }, "type": "object", "required": [ "workflowId", "status", "pipelineId" ], "title": "InferenceWorkflowStatus", "description": "Status information for ML inference workflow." }, "LogSubmissionResponse": { "properties": { "status": { "type": "string", "title": "Status", "description": "Processing status (e.g., 'success', 'logged')" }, "message": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Message", "description": "Optional response message" } }, "type": "object", "required": [ "status" ], "title": "LogSubmissionResponse", "description": "Response model for frontend log submission.\n\nConfirms that the log was received and processed." }, "ModelEndpointsMetadataResponse": { "items": { "$ref": "#/components/schemas/PipelineMetadata" }, "type": "array", "title": "ModelEndpointsMetadataResponse", "description": "Response model for GET /model-endpoints-metadata endpoint.\n\nReturns ML model endpoint configuration metadata.\nUses RootModel to return a list of PipelineMetadata objects." }, "ModelInfo": { "properties": { "name": { "type": "string", "title": "Name" }, "version": { "type": "string", "title": "Version" } }, "type": "object", "required": [ "name", "version" ], "title": "ModelInfo", "description": "Model information for API response." }, "ParentWorkflowStatus": { "properties": { "workflowId": { "type": "string", "title": "Workflowid" }, "status": { "type": "string", "title": "Status" }, "progressPercentage": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Progresspercentage" }, "createdAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Createdat" }, "completedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Completedat" }, "failedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Failedat" }, "errorMessage": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Errormessage" }, "malwareDetected": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Malwaredetected" } }, "type": "object", "required": [ "workflowId", "status" ], "title": "ParentWorkflowStatus", "description": "Status information for parent (orchestrator) workflow." }, "PipelineDetail": { "properties": { "pipelineId": { "type": "string", "title": "Pipelineid", "description": "UUID of the pipeline" }, "pipelineName": { "type": "string", "title": "Pipelinename", "description": "Pipeline name" }, "createdBy": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Createdby", "description": "Creator of the pipeline" }, "creationDate": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Creationdate", "description": "ISO formatted creation date" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Description", "description": "Pipeline description" }, "jobName": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Jobname", "description": "Job name for the pipeline" }, "version": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Version", "description": "Pipeline version" }, "dataset": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Dataset", "description": "Dataset used for training" }, "identifiable": { "items": { "type": "string" }, "type": "array", "title": "Identifiable", "description": "List of identifiable classes" }, "metrics": { "items": { "additionalProperties": true, "type": "object" }, "type": "array", "title": "Metrics", "description": "Performance metrics" }, "models": { "items": { "$ref": "#/components/schemas/PipelineStep" }, "type": "array", "title": "Models", "description": "Ordered list of model steps in the pipeline" } }, "type": "object", "required": [ "pipelineId", "pipelineName", "models" ], "title": "PipelineDetail", "description": "Complete pipeline configuration with metadata and steps.\n\nRepresents a single ML pipeline with all its models and metadata." }, "PipelineMetadata": { "properties": { "createdBy": { "type": "string", "title": "Createdby", "description": "Creator of the pipeline" }, "creationDate": { "type": "string", "title": "Creationdate", "description": "ISO formatted creation date" }, "dataset": { "type": "string", "title": "Dataset", "description": "Dataset used for training" }, "description": { "type": "string", "title": "Description", "description": "Pipeline description" }, "identifiable": { "items": { "type": "string" }, "type": "array", "title": "Identifiable", "description": "List of identifiable classes" }, "jobName": { "type": "string", "title": "Jobname", "description": "Job name for the pipeline" }, "metrics": { "items": { "additionalProperties": true, "type": "object" }, "type": "array", "title": "Metrics", "description": "Performance metrics" }, "modelName": { "type": "string", "title": "Modelname", "description": "Model display name" }, "models": { "items": { "type": "string" }, "type": "array", "title": "Models", "description": "List of model names in the pipeline" }, "pipelineName": { "type": "string", "title": "Pipelinename", "description": "Pipeline name" }, "pipelineId": { "type": "string", "title": "Pipelineid", "description": "Pipeline UUID" }, "version": { "type": "string", "title": "Version", "description": "Pipeline version" }, "default": { "type": "boolean", "title": "Default", "description": "Whether this is the default pipeline", "default": false } }, "type": "object", "required": [ "createdBy", "creationDate", "dataset", "description", "jobName", "modelName", "models", "pipelineName", "pipelineId", "version" ], "title": "PipelineMetadata", "description": "Model for individual pipeline metadata entry.\n\nContains pipeline configuration, model information, and metadata." }, "PipelineStep": { "properties": { "modelId": { "type": "string", "title": "Modelid", "description": "UUID of the model" }, "modelName": { "type": "string", "title": "Modelname", "description": "Name of the model" }, "version": { "type": "string", "title": "Version", "description": "Model version" }, "endpoint": { "type": "string", "title": "Endpoint", "description": "API endpoint URL" }, "apiKey": { "type": "string", "title": "Apikey", "description": "API key for authentication" }, "contentType": { "type": "string", "title": "Contenttype", "description": "Content type for requests" }, "deploymentPlatform": { "type": "string", "title": "Deploymentplatform", "description": "Platform where model is deployed" }, "endpointName": { "type": "string", "title": "Endpointname", "description": "Endpoint identifier name" }, "requestFunction": { "type": "string", "title": "Requestfunction", "description": "Function used to make requests" }, "step": { "type": "integer", "title": "Step", "description": "Step number in the pipeline sequence" } }, "type": "object", "required": [ "modelId", "modelName", "version", "endpoint", "apiKey", "contentType", "deploymentPlatform", "endpointName", "requestFunction", "step" ], "title": "PipelineStep", "description": "Individual model step in a pipeline.\n\nRepresents a single ML model within a pipeline's execution sequence." }, "PipelinesResponse": { "properties": { "pipelines": { "items": { "$ref": "#/components/schemas/PipelineDetail" }, "type": "array", "title": "Pipelines", "description": "List of available pipeline configurations" } }, "type": "object", "required": [ "pipelines" ], "title": "PipelinesResponse", "description": "Response model for GET /pipelines endpoint.\n\nReturns available ML pipelines for seed classification." }, "PixelBoundingBox": { "properties": { "topX": { "type": "integer", "title": "Topx", "description": "Top-left X coordinate (pixels)" }, "topY": { "type": "integer", "title": "Topy", "description": "Top-left Y coordinate (pixels)" }, "bottomX": { "type": "integer", "title": "Bottomx", "description": "Bottom-right X coordinate (pixels)" }, "bottomY": { "type": "integer", "title": "Bottomy", "description": "Bottom-right Y coordinate (pixels)" } }, "type": "object", "required": [ "topX", "topY", "bottomX", "bottomY" ], "title": "PixelBoundingBox", "description": "Bounding box coordinates in pixel values.\n\nUsed after converting from normalized coordinates (0.0-1.0)\nto absolute pixel positions." }, "PredictionLabelScore": { "properties": { "label": { "type": "string", "title": "Label", "description": "Classification label or species name" }, "score": { "type": "number", "maximum": 1.0, "minimum": 0.0, "title": "Score", "description": "Confidence score between 0.0 and 1.0" } }, "type": "object", "required": [ "label", "score" ], "title": "PredictionLabelScore", "description": "Universal prediction model for all inference results.\n\nRepresents a single prediction with a label (species name) and confidence score.\nUsed across all model types: SWIN classifiers, Triton inference, and ensemble models.\n\nConsolidates TopNPredictionAPI, TopNPredictionCleaned, and SeedPrediction." }, "ProcessingStages": { "properties": { "uploaded": { "type": "boolean", "title": "Uploaded" }, "defenderScanning": { "type": "boolean", "title": "Defenderscanning" }, "defenderScanned": { "type": "boolean", "title": "Defenderscanned" }, "sanitizing": { "type": "boolean", "title": "Sanitizing" }, "sanitized": { "type": "boolean", "title": "Sanitized" } }, "type": "object", "required": [ "uploaded", "defenderScanning", "defenderScanned", "sanitizing", "sanitized" ], "title": "ProcessingStages", "description": "Processing pipeline stages completion status." }, "ProcessingTimestamps": { "properties": { "uploadedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Uploadedat" }, "defenderScanStartedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Defenderscanstartedat" }, "defenderScanCompletedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Defenderscancompletedat" }, "sanitizationStartedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Sanitizationstartedat" }, "sanitizationCompletedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Sanitizationcompletedat" }, "completedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Completedat" }, "failedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Failedat" } }, "type": "object", "title": "ProcessingTimestamps", "description": "Timestamps for each processing stage." }, "ProcessingWorkflowStatus": { "properties": { "status": { "type": "string", "title": "Status" }, "stages": { "$ref": "#/components/schemas/ProcessingStages" }, "timestamps": { "$ref": "#/components/schemas/ProcessingTimestamps" }, "defenderScanResult": { "anyOf": [ { "$ref": "#/components/schemas/DefenderScanResult" }, { "type": "null" } ] }, "blobUrls": { "$ref": "#/components/schemas/BlobUrls" }, "errorMessage": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Errormessage" }, "errorDetails": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Errordetails" } }, "type": "object", "required": [ "status", "stages", "timestamps", "blobUrls" ], "title": "ProcessingWorkflowStatus", "description": "Detailed status for image processing workflow." }, "RegistrationStatusResponse": { "properties": { "isRegistered": { "type": "boolean", "title": "Isregistered", "description": "Whether the user is registered in the system" } }, "type": "object", "required": [ "isRegistered" ], "title": "RegistrationStatusResponse", "description": "Response model for GET /is-registered endpoint.\n\nChecks if the user is registered in the system." }, "SeedData": { "properties": { "seeds": { "items": { "$ref": "#/components/schemas/SeedItem" }, "type": "array", "title": "Seeds", "description": "List of seed species" } }, "type": "object", "required": [ "seeds" ], "title": "SeedData", "description": "Wrapper for seed data list." }, "SeedDataResponse": { "$ref": "#/components/schemas/SeedData", "title": "SeedDataResponse", "description": "Response model for GET /seeds endpoint.\n\nReturns seed species data for frontend selection.\nUses RootModel wrapping SeedData for proper camelCase serialization." }, "SeedItem": { "properties": { "seedId": { "type": "string", "title": "Seedid", "description": "UUID of the seed" }, "nameCode": { "type": "string", "title": "Namecode", "description": "Short code name for the seed" }, "family": { "type": "string", "title": "Family", "description": "Taxonomic family" }, "genus": { "type": "string", "title": "Genus", "description": "Taxonomic genus" }, "species": { "type": "string", "title": "Species", "description": "Taxonomic species" }, "subspecies": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Subspecies", "description": "Taxonomic subspecies" }, "variety": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Variety", "description": "Taxonomic variety" }, "author": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Author", "description": "Author citation" }, "url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Url", "description": "Reference URL" }, "seedMetadata": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "title": "Seedmetadata", "description": "Additional seed metadata" } }, "type": "object", "required": [ "seedId", "nameCode", "family", "genus", "species" ], "title": "SeedItem", "description": "Individual seed entry with taxonomic information.\n\nRepresents a single seed species with identification metadata." }, "TrayCode": { "type": "string", "enum": [ "A", "B", "C", "D", "E" ], "title": "TrayCode", "description": "Valid tray codes for sample identification." }, "UpdateFolderRequest": { "properties": { "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Name" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Description" } }, "type": "object", "title": "UpdateFolderRequest", "description": "Request model for updating a folder's name and/or description.\n\nBoth fields are optional - update only the fields provided.\nName validation is performed in the service layer.\nDescription is sanitized on the client side." }, "UpdateFolderResponse": { "properties": { "id": { "type": "string", "title": "Id" }, "message": { "type": "string", "title": "Message" } }, "type": "object", "required": [ "id", "message" ], "title": "UpdateFolderResponse", "description": "Response model for folder update operations." }, "UserIdResponse": { "properties": { "userId": { "type": "string", "title": "Userid", "description": "User's unique identifier (OID from Azure AD)" } }, "type": "object", "required": [ "userId" ], "title": "UserIdResponse", "description": "Response model for POST /get-user-id endpoint.\n\nReturns the authenticated user's ID." }, "ValidationError": { "properties": { "loc": { "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "type": "array", "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" } }, "type": "object", "required": [ "loc", "msg", "type" ], "title": "ValidationError" }, "VersionResponse": { "properties": { "version": { "type": "string", "title": "Version", "description": "API version string" } }, "type": "object", "required": [ "version" ], "title": "VersionResponse", "description": "Response model for GET /version endpoint.\n\nReturns the current API version." }, "WorkflowAuthorization": { "properties": { "userId": { "type": "string", "title": "Userid" }, "isOwner": { "type": "boolean", "title": "Isowner" }, "isCfiaAdmin": { "type": "boolean", "title": "Iscfiaadmin" } }, "type": "object", "required": [ "userId", "isOwner", "isCfiaAdmin" ], "title": "WorkflowAuthorization", "description": "Authorization metadata for workflow access." }, "WorkflowStatusResponse": { "properties": { "workflowId": { "type": "string", "title": "Workflowid", "description": "The queried workflow ID" }, "workflowType": { "type": "string", "title": "Workflowtype", "description": "Workflow type: 'parent', 'processing', or 'inference'" }, "imageId": { "type": "string", "title": "Imageid", "description": "Associated picture UUID" }, "overallStatus": { "type": "string", "title": "Overallstatus", "description": "High-level status: 'pending', 'in_progress', 'completed', 'failed'" }, "authorization": { "$ref": "#/components/schemas/WorkflowAuthorization", "description": "Authorization metadata" }, "parentWorkflow": { "anyOf": [ { "$ref": "#/components/schemas/ParentWorkflowStatus" }, { "type": "null" } ], "description": "Parent workflow status (if exists)" }, "processingWorkflow": { "anyOf": [ { "$ref": "#/components/schemas/ProcessingWorkflowStatus" }, { "type": "null" } ], "description": "Processing workflow details (if exists)" }, "inferenceWorkflow": { "anyOf": [ { "$ref": "#/components/schemas/InferenceWorkflowStatus" }, { "type": "null" } ], "description": "Inference workflow details (if exists)" }, "errorMessage": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Errormessage", "description": "Error message (if failed)" }, "errorDetails": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Errordetails", "description": "Detailed error info (if failed)" } }, "type": "object", "required": [ "workflowId", "workflowType", "imageId", "overallStatus", "authorization" ], "title": "WorkflowStatusResponse", "description": "Comprehensive workflow status response.\n\nReturned by GET /workflow/{workflow_id}/status endpoint.\nProvides complete status tracking for image processing and inference workflows.\n\nThe response includes:\n- Overall workflow status and type\n- Parent workflow orchestration status\n- Image processing pipeline details and timestamps\n- ML inference workflow status\n- Authorization metadata" } } } }