-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathopenapi.json
More file actions
1 lines (1 loc) · 120 KB
/
Copy pathopenapi.json
File metadata and controls
1 lines (1 loc) · 120 KB
1
{"openapi":"3.1.0","info":{"title":"NiChart API","summary":"Backend API for the NiChart medical-imaging pipeline platform.","description":"Provides endpoints for managing projects, uploading imaging data, running containerised processing pipelines (locally via Docker or on AWS Batch), and retrieving results. \n\n**Authentication**: In cloud mode the server uses a BFF OAuth2 flow with Cognito. Tokens are stored in httpOnly cookies — never in JS-accessible storage. Navigate the browser to ``GET /auth/login`` to begin sign-in; all subsequent API requests carry the session cookie automatically. In local mode (``NICHART_EXECUTION_MODE=local``) authentication is bypassed.","contact":{"name":"CBICA","url":"https://github.com/CBICA/NiChart_Project"},"license":{"name":"MIT"},"version":"0.1.0"},"paths":{"/auth/login":{"get":{"tags":["Auth"],"summary":"Begin OAuth2 sign-in","description":"Generates a CSRF ``state`` and PKCE ``code_verifier``/``code_challenge``, stores them in short-lived httpOnly cookies, then redirects the browser to the Cognito Hosted UI authorization endpoint. Navigate the browser (not a fetch/XHR) to this URL to start sign-in.","operationId":"login_auth_login_get","responses":{"307":{"description":"Successful Response"},"302":{"description":"Redirect to Cognito authorize endpoint."},"503":{"description":"Cognito domain not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}}}},"/auth/callback":{"get":{"tags":["Auth"],"summary":"OAuth2 callback — exchange code for session","description":"Cognito redirects here after the user authenticates. Validates the CSRF ``state`` cookie, exchanges the authorization code for tokens (using the PKCE verifier + client secret), verifies the ID token, sets the ``session`` cookie, and redirects to the frontend. This endpoint is called by the browser following a redirect — not by JS fetch.","operationId":"callback_auth_callback_get","responses":{"307":{"description":"Successful Response"},"302":{"description":"Redirect to frontend after successful authentication."},"400":{"description":"State mismatch or missing parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"401":{"description":"Token validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"502":{"description":"Cognito token exchange failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}}}},"/auth/refresh":{"post":{"tags":["Auth"],"summary":"Refresh the session","description":"Uses the ``refresh_token`` httpOnly cookie to obtain a new ID token from Cognito and updates the ``session`` cookie. Returns updated user claims on success. Returns 401 when there is no refresh token or it has expired — the client should redirect to ``GET /auth/login``.","operationId":"refresh_auth_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"401":{"description":"No refresh token or token expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"502":{"description":"Cognito token refresh failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Return current user claims","description":"Reads the ``session`` cookie and returns safe user claims if the session is valid. The frontend should poll this endpoint to determine auth state on page load. Returns 401 when there is no session or it has expired — the client should redirect to ``GET /auth/login``. In local mode, always returns the OS user without a cookie.","operationId":"me_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"401":{"description":"No valid session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}}}},"/auth/logout":{"get":{"tags":["Auth"],"summary":"Sign out","description":"Clears the ``session`` and ``refresh_token`` cookies, then redirects to the Cognito logout URL (which itself redirects to the configured frontend URL). Navigate the browser to this URL — do not call it via fetch/XHR.","operationId":"logout_auth_logout_get","responses":{"307":{"description":"Successful Response"},"302":{"description":"Redirect to Cognito logout endpoint."}}}},"/catalog/pipelines":{"get":{"tags":["Catalog"],"summary":"List all enabled pipelines","description":"Returns a summary of every pipeline defined in the resources/pipelines directory. Use ``GET /catalog/pipelines/{pipeline_id}`` to retrieve the full definition.","operationId":"list_pipelines_catalog_pipelines_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PipelineSummary"},"type":"array","title":"Response List Pipelines Catalog Pipelines Get"}}}}}}},"/catalog/pipelines/{pipeline_id}":{"get":{"tags":["Catalog"],"summary":"Get pipeline detail","description":"Returns the full pipeline definition: description, categories, data requirements, and ordered steps with their tool and I/O mappings.","operationId":"get_pipeline_catalog_pipelines__pipeline_id__get","parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineDetail"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/catalog/modalities":{"get":{"tags":["Catalog"],"summary":"List recognized imaging modalities","description":"Returns every imaging modality the platform recognizes — the code (also the study subdirectory name and the key used in NIfTI uploads and pipeline ``needs_<code>`` requirements) and a human label. Clients should read this rather than hard-coding the modality list.","operationId":"list_modalities_catalog_modalities_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ModalityInfo"},"type":"array","title":"Response List Modalities Catalog Modalities Get"}}}}}}},"/catalog/tools":{"get":{"tags":["Catalog"],"summary":"List all available tools","description":"Returns a summary of every tool defined in the resources/tools directory.","operationId":"list_tools_catalog_tools_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolSummary"},"type":"array","title":"Response List Tools Catalog Tools Get"}}}}}}},"/catalog/tools/{tool_id}":{"get":{"tags":["Catalog"],"summary":"Get tool detail","description":"Returns the full tool specification: inputs, outputs, resource requirements, configurable parameters, and the per-subject time estimate used for queue-drain calculations.","operationId":"get_tool_catalog_tools__tool_id__get","parameters":[{"name":"tool_id","in":"path","required":true,"schema":{"type":"string","title":"Tool Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolDetail"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/catalog/centiles/feature-metadata":{"get":{"tags":["Catalog"],"summary":"Get centile feature display metadata","description":"Returns per-variable display metadata that controls how variables appear in the centile plotting variable selector. Only variables with non-default behaviour are included in the ``features`` map; variables absent from the map should be treated as visible and enabled. \n\nThis metadata is maintained in a static server-side config file (``resources/reference_data/centiles/feature_metadata.yaml``) so that display policy is never hard-coded in the frontend.","operationId":"get_centile_feature_metadata_catalog_centiles_feature_metadata_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CentileFeatureMetadataResponse"}}}}}}},"/catalog/resources/{path}":{"get":{"tags":["Catalog"],"summary":"Download a public resource file","description":"Serves static resource files from the server's ``resources/`` directory: atlases, label maps, normative data CSVs, and other reference data. No authentication required. Responses are cached for 24 hours.","operationId":"get_resource_catalog_resources__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/octet-stream":{}}},"400":{"description":"Invalid resource path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/catalog/docs":{"get":{"tags":["Docs"],"summary":"List documentation topics","description":"Returns a summary of every documentation topic in resources/docs/. Each topic may cover one or more related pipelines (e.g. harmonized and base DLMUSE variants share a single 'dlmuse' topic). No authentication required.","operationId":"list_docs_catalog_docs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DocTopicSummary"},"type":"array","title":"Response List Docs Catalog Docs Get"}}}}}}},"/catalog/docs/{docs_id}":{"get":{"tags":["Docs"],"summary":"Get documentation topic manifest","description":"Returns the manifest for a documentation topic: its sections, audience tags, and the pipeline IDs it covers. Use the ``file`` field of each section with ``GET /catalog/docs/{docs_id}/{file}`` to fetch the actual content. No authentication required.","operationId":"get_docs_manifest_catalog_docs__docs_id__get","parameters":[{"name":"docs_id","in":"path","required":true,"schema":{"type":"string","title":"Docs Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocManifest"}}}},"404":{"description":"Topic or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/catalog/docs/{docs_id}/{path}":{"get":{"tags":["Docs"],"summary":"Fetch a documentation file","description":"Serves any file within the documentation topic folder: markdown prose, images, or JSON data. Relative image references in markdown files resolve naturally to further requests under this same path prefix. Path traversal outside the topic folder is rejected. No authentication required.","operationId":"get_docs_file_catalog_docs__docs_id___path__get","parameters":[{"name":"docs_id","in":"path","required":true,"schema":{"type":"string","title":"Docs Id"}},{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/octet-stream":{}}},"404":{"description":"Topic or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cloud/status":{"get":{"tags":["Cloud"],"summary":"Cloud queue status","description":"Returns the number of running and pending jobs on the AWS Batch queue and a rough estimate of how long until the queue drains. In local mode all job-count fields are null. No authentication required.","operationId":"cloud_status_cloud_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudStatusResponse"}}}},"503":{"description":"Could not reach the AWS Batch API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}}}},"/projects":{"get":{"tags":["Projects"],"summary":"List projects","description":"Returns all projects owned by the authenticated user.","operationId":"list_projects_projects_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Project"},"type":"array","title":"Response List Projects Projects Get"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}}},"post":{"tags":["Projects"],"summary":"Create a project","description":"Creates a new project directory. The project name is used as the directory name and URL path segment. Must match ``^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$``. \n\nIn cloud mode a retention heartbeat is written to S3 as part of creation, which starts the PROJECT_RETENTION_DAYS countdown.","operationId":"create_project_projects_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"Bad request (e.g. invalid project name).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"409":{"description":"A project with that name already exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}":{"delete":{"tags":["Projects"],"summary":"Delete a project","description":"Permanently deletes the project and all its data. Irreversible.","operationId":"delete_project_projects__project_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/retention":{"get":{"tags":["Projects"],"summary":"Get project retention info","description":"Returns the expiry timestamp for the project. The countdown resets each time ``POST /projects/{project_id}/retention/refresh`` is called. \n\nReturns 404 when the heartbeat object is absent — this is expected for projects created before this feature shipped; the Lambda sweep will backfill the heartbeat on its next daily run, after which this endpoint will return a value.\n\nCloud mode only — returns 404 in local mode.","operationId":"get_retention_projects__project_id__retention_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetentionInfo"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project not found or heartbeat not yet written (legacy project or creation race).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/retention/refresh":{"post":{"tags":["Projects"],"summary":"Refresh project retention","description":"Resets the retention countdown by overwriting the heartbeat marker with the current timestamp. Returns the new expiry time (now + PROJECT_RETENTION_DAYS). \n\nIf the heartbeat was previously absent (legacy project) this call creates it, which is equivalent to a fresh creation. \n\nCloud mode only — returns 404 in local mode.","operationId":"refresh_retention_projects__project_id__retention_refresh_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetentionInfo"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project not found or heartbeat not yet written (legacy project or creation race).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files":{"get":{"tags":["Files"],"summary":"List project files","description":"Returns a flat listing of all visible files and directories in the project. Internal staging (``_upload/``) and working (``_working/``) directories are excluded.","operationId":"list_files_projects__project_id__files_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryTree"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Files"],"summary":"Delete a file or directory","description":"Permanently deletes the file or directory at ``path``.","operationId":"delete_file_projects__project_id__files_delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"path","in":"query","required":true,"schema":{"type":"string","description":"Path relative to the project root.","title":"Path"},"description":"Path relative to the project root."}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/download":{"get":{"tags":["Files"],"summary":"Download a file or directory","description":"Download a single file, or a directory as a streaming zip archive when ``zip=true``. The ``path`` is relative to the project root and is validated to prevent traversal.","operationId":"download_file_projects__project_id__files_download_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"path","in":"query","required":true,"schema":{"type":"string","description":"Path relative to the project root.","title":"Path"},"description":"Path relative to the project root."},{"name":"zip","in":"query","required":false,"schema":{"type":"boolean","description":"Stream target directory as a zip archive.","default":false,"title":"Zip"},"description":"Stream target directory as a zip archive."}],"responses":{"200":{"description":"File content or zip stream.","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/archive":{"post":{"tags":["Files"],"summary":"Bundle a multi-file/directory selection into one zip","description":"Bundles an arbitrary set of files and/or directories into a single zip archive, preserving each entry's path relative to the project root. Useful for downloading a multi-selection from the file browser as one archive instead of one request per item. Named to avoid 'download' in the URL, which some ad-blocker/privacy extensions filter.","operationId":"create_archive_projects__project_id__files_archive_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadZipRequest"}}}},"responses":{"200":{"description":"Zip stream.","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/upload/nifti":{"post":{"tags":["Files"],"summary":"Upload NIfTI file(s) to staging","description":"Accepts one or more ``.nii`` / ``.nii.gz`` files (including a folder upload, where filenames carry relative directory paths). Files land in the project staging area and the server returns its best-effort MRID and modality inference. If two entries flatten to the same filename, the first one wins and the rest are reported in ``skipped_duplicates``. Follow up with the commit endpoint to move them into the project.","operationId":"upload_nifti_projects__project_id__files_upload_nifti_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_nifti_projects__project_id__files_upload_nifti_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NiftiStagingResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/upload/nifti/zip":{"post":{"tags":["Files"],"summary":"Upload a zip of NIfTI files to staging","description":"Accepts a ``.zip`` archive containing ``.nii`` / ``.nii.gz`` files (subdirectories are searched recursively; non-NIfTI entries are ignored). Files land in the project staging area and the server returns its best-effort MRID and modality inference. Directory components in the archive path contribute to modality detection (e.g. ``fl/subject001.nii.gz`` infers modality ``fl``). Nested directories are flattened into a single staging folder; if two entries flatten to the same filename, the first one wins and the rest are reported in ``skipped_duplicates``. Follow up with the commit endpoint to move them into the project.","operationId":"upload_nifti_zip_projects__project_id__files_upload_nifti_zip_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_nifti_zip_projects__project_id__files_upload_nifti_zip_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NiftiStagingResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/stage/{staging_id}/commit":{"post":{"tags":["Files"],"summary":"Commit staged NIfTI files","description":"Confirms MRID and modality mappings for staged files and moves them into their final locations. Every staged file must appear in ``mappings``. If a subject already has a file at the target modality/MRID slot, the new upload is left in place there and the mapping is reported in ``skipped`` instead of ``committed`` — existing scans are never overwritten by a re-upload.","operationId":"commit_nifti_projects__project_id__files_stage__staging_id__commit_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"staging_id","in":"path","required":true,"schema":{"type":"string","title":"Staging Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NiftiCommitRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NiftiCommitResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/stage/{staging_id}":{"delete":{"tags":["Files"],"summary":"Discard a staging area","description":"Deletes all files in the staging area without committing.","operationId":"discard_staging_projects__project_id__files_stage__staging_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"staging_id","in":"path","required":true,"schema":{"type":"string","title":"Staging Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/upload/csv":{"post":{"tags":["Files"],"summary":"Upload participants CSV","description":"Replaces ``participants/participants.csv`` with the uploaded file. The file is stored as-is; must have at least an ``MRID`` column.","operationId":"upload_csv_projects__project_id__files_upload_csv_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_csv_projects__project_id__files_upload_csv_post"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/upload/bids":{"post":{"tags":["Files"],"summary":"Upload a BIDS dataset","description":"Accepts a ``.zip`` archive containing a BIDS-layout dataset, extracts it, and reorganises it into the NiChart project layout. ``sub-{id}_T1w.nii.gz`` → ``t1/{id}.nii.gz``, ``_FLAIR`` → ``fl/``, etc. A ``participants.tsv`` at the archive root is converted to ``participants/participants.csv``.","operationId":"upload_bids_projects__project_id__files_upload_bids_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_bids_projects__project_id__files_upload_bids_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/upload/bids/files":{"post":{"tags":["Files"],"summary":"Upload individual BIDS files","description":"Accepts one or more files as multipart form data representing a BIDS dataset. Filenames may include relative sub-paths (e.g. ``sub-01/anat/sub-01_T1w.nii.gz``) and are validated for path traversal before writing. Reorganisation into the NiChart project layout is identical to the ZIP upload.","operationId":"upload_bids_files_projects__project_id__files_upload_bids_files_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_bids_files_projects__project_id__files_upload_bids_files_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/upload/idat":{"post":{"tags":["Files"],"summary":"Upload IDAT files","description":"Accepts a ``.zip`` of ``.idat`` files. Extracted to ``idat/`` in the project.","operationId":"upload_idat_projects__project_id__files_upload_idat_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_idat_projects__project_id__files_upload_idat_post"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/upload/idat/files":{"post":{"tags":["Files"],"summary":"Upload individual IDAT files","description":"Accepts one or more ``.idat`` files as multipart form data. Filenames must be flat (no directory separators) and end with ``.idat``. Each filename is validated before any file is written to ``idat/``.","operationId":"upload_idat_files_projects__project_id__files_upload_idat_files_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_idat_files_projects__project_id__files_upload_idat_files_post"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/participants":{"get":{"tags":["Files"],"summary":"Get participants list","description":"Returns the contents of ``participants/participants.csv`` as JSON rows.","operationId":"get_participants_projects__project_id__participants_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantsList"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}},"description":"Not Found"},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Files"],"summary":"Replace participants list","description":"Overwrites ``participants/participants.csv`` with the provided rows. Full replacement — rows not included are removed.","operationId":"update_participants_projects__project_id__participants_patch","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantsUpdate"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/participants/template":{"get":{"tags":["Files"],"summary":"Download participants CSV template","description":"Returns a CSV template pre-populated with all MRIDs detected from committed NIfTI files in the project's modality directories. Rows already present in participants.csv retain their values; newly detected MRIDs appear with empty additional columns. Use this as a starting point for filling in demographic or clinical data before uploading.","operationId":"get_participants_template_projects__project_id__participants_template_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/readiness/{pipeline_id}":{"get":{"tags":["Files"],"summary":"Check pipeline readiness","description":"Evaluates whether the project has the data required to run a pipeline. Returns a per-requirement status so the client can guide users to upload missing imaging data or fill missing participants.csv columns. Imaging checks scan the modality directories (``t1/``, ``fl/``, etc.) for NIfTI files. CSV checks verify that required columns exist and are non-empty for every subject.","operationId":"check_readiness_projects__project_id__readiness__pipeline_id__get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadinessReport"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/provenance":{"get":{"tags":["Files"],"summary":"Verify step provenance","description":"Scans all ``_provenance.json`` files written by the pipeline executor after each successful step, then checks whether any recorded input paths have been modified since the step finished. A 'dirty' entry means the step's cached result may be stale and the step should re-run. A 'missing_inputs' entry means a required input no longer exists. Returns 'no_provenance' summary when no steps have completed yet.","operationId":"verify_provenance_projects__project_id__provenance_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvenanceReport"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid or unsafe path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/dicom/upload":{"post":{"tags":["DICOM"],"summary":"Upload DICOM zip to staging","description":"Accepts a single ``.zip`` archive of DICOM files. The archive is extracted with symlink and path-traversal checks into a private staging area. Use the returned ``staging_id`` to inspect series and submit a conversion job.","operationId":"upload_dicom_projects__project_id__files_dicom_upload_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_dicom_projects__project_id__files_dicom_upload_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DicomStagingResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or staging area not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/dicom/upload/files":{"post":{"tags":["DICOM"],"summary":"Upload individual DICOM files to staging","description":"Accepts one or more raw DICOM files as multipart form data. Each filename is validated (no path traversal, no directory separators). Files are staged identically to the ZIP upload — use the returned ``staging_id`` with the inspect and convert endpoints.","operationId":"upload_dicom_files_projects__project_id__files_dicom_upload_files_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_dicom_files_projects__project_id__files_dicom_upload_files_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DicomStagingResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or staging area not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/dicom/{staging_id}/series":{"get":{"tags":["DICOM"],"summary":"Inspect DICOM series","description":"Reads DICOM headers (via pydicom) in the staging area and returns a list of detected series with their description, modality, study date, and file count. No conversion happens at this step — it is safe to call multiple times.","operationId":"list_dicom_series_projects__project_id__files_dicom__staging_id__series_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"staging_id","in":"path","required":true,"schema":{"type":"string","title":"Staging Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DicomSeriesListing"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or staging area not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/dicom/{staging_id}/convert":{"post":{"tags":["DICOM"],"summary":"Submit DICOM conversion job","description":"Accepts a mapping of DICOM series UIDs to NiChart modality labels and submits a dcm2niix conversion job for each selected series. Series not listed are ignored. Returns a ``run_id`` for polling via ``GET /jobs/pipelines/{run_id}``.","operationId":"convert_dicom_projects__project_id__files_dicom__staging_id__convert_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"staging_id","in":"path","required":true,"schema":{"type":"string","title":"Staging Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DicomConvertRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DicomConvertResult"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or staging area not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/files/dicom/{staging_id}":{"delete":{"tags":["DICOM"],"summary":"Discard DICOM staging area","description":"Permanently deletes the staged DICOM files without converting them. Staging areas are also auto-cleaned after ``NICHART_STAGING_TTL_HOURS``.","operationId":"discard_dicom_staging_projects__project_id__files_dicom__staging_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"staging_id","in":"path","required":true,"schema":{"type":"string","title":"Staging Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied to this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or staging area not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/jobs/pipelines":{"post":{"tags":["Jobs"],"summary":"Submit a pipeline run","description":"Starts an asynchronous pipeline run for the given project. The server spawns a background task that executes each pipeline step in order, polling the job backend between steps. Use the returned ``run_id`` to track progress via ``GET /jobs/pipelines/{run_id}``.","operationId":"submit_pipeline_projects__project_id__jobs_pipelines_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineRunSubmit"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineRunDetail"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Invalid pipeline ID or parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project or pipeline not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/jobs/pipelines":{"get":{"tags":["Jobs"],"summary":"List pipeline runs","description":"Returns the authenticated user's pipeline runs in reverse-chronological order. Pass ``project_id`` to filter to a single project.","operationId":"list_pipeline_runs_jobs_pipelines_get","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter runs to this project.","title":"Project Id"},"description":"Filter runs to this project."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Maximum number of runs to return.","default":50,"title":"Limit"},"description":"Maximum number of runs to return."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PipelineRunSummary"},"title":"Response List Pipeline Runs Jobs Pipelines Get"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/jobs/pipelines/finished":{"get":{"tags":["Jobs"],"summary":"Poll for newly finished pipeline runs","description":"Returns all pipeline runs that reached a terminal state (``succeeded`` or ``failed``) since the last time this endpoint was called by the authenticated user. The server stores a per-user cursor so only genuinely new completions are returned on each call. \n\n**First call**: the cursor defaults to epoch, so all existing terminal runs are returned — useful for bootstrapping a UI on first load. \n\nDesigned for lightweight notification polling (e.g. every 10–30 seconds). For full run history use ``GET /jobs/pipelines``.","operationId":"get_finished_pipeline_runs_jobs_pipelines_finished_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinishedRunsResponse"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}}}},"/jobs/pipelines/{run_id}":{"get":{"tags":["Jobs"],"summary":"Get pipeline run detail","description":"Returns the full run record including per-step status, timestamps, and any error messages. In cloud mode, while a step is actively waiting on the Batch queue, ``jobs_ahead`` and ``estimated_wait_seconds`` are populated by querying all jobs submitted to the queue before this one. These fields are null in local mode or once a job has left the queue.","operationId":"get_pipeline_run_jobs_pipelines__run_id__get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineRunDetail"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Jobs"],"summary":"Cancel a pipeline run","description":"Requests cancellation of a running pipeline. The currently executing tool step is cancelled on the backend; subsequent steps will not be submitted. No-op if the run is already in a terminal state.","operationId":"cancel_pipeline_run_jobs_pipelines__run_id__delete","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/jobs/pipelines/{run_id}/logs":{"get":{"tags":["Jobs"],"summary":"Get pipeline run logs","description":"Returns concatenated log output from all steps completed so far. Poll this endpoint alongside the status endpoint during a run.","operationId":"get_pipeline_logs_jobs_pipelines__run_id__logs_get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineRunLogs"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/jobs/pipelines/{run_id}/steps/{step_id}/chunks/{chunk_idx}/logs":{"get":{"tags":["Jobs"],"summary":"Get logs for a single parallel chunk","description":"Returns the captured log output for one chunk of a parallelised pipeline step. Only available after the chunk has started executing. Use ``GET /jobs/pipelines/{run_id}/logs`` to retrieve all chunks concatenated.","operationId":"get_chunk_logs_jobs_pipelines__run_id__steps__step_id__chunks__chunk_idx__logs_get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"step_id","in":"path","required":true,"schema":{"type":"string","title":"Step Id"}},{"name":"chunk_idx","in":"path","required":true,"schema":{"type":"integer","title":"Chunk Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"string","title":"Response Get Chunk Logs Jobs Pipelines Run Id Steps Step Id Chunks Chunk Idx Logs Get"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"400":{"description":"Step was not run in parallel chunks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Run, step, or chunk not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/results":{"get":{"tags":["Results"],"summary":"List available pipeline results","description":"Returns a summary for every pipeline that declares a ``results:`` section in its YAML. Summaries indicate which output types are present in the project. Use this to discover which pipelines have results to visualize — one call covers all pipelines.","operationId":"list_results_projects__project_id__results_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PipelineResultSummary"},"title":"Response List Results Projects Project Id Results Get"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Project not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/results/{pipeline_id}":{"get":{"tags":["Results"],"summary":"Get full result detail for a pipeline","description":"Returns the complete result structure for one pipeline: feature columns, label map (column → segmentation label IDs for overlay rendering), per-subject file availability, subject completeness, and resource paths for the atlas NIfTI and normative data CSV. \n\n**Atlas and normative data** are fetched via ``GET /catalog/resources/{path}`` (public, no auth required, cached 24 h).","operationId":"get_result_detail_projects__project_id__results__pipeline_id__get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineResultDetail"}}}},"401":{"description":"Missing or invalid token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"403":{"description":"Access denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"Pipeline or project not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"tags":["Health"],"summary":"Health check","description":"Returns server status and the active execution mode. No authentication required.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"BatchFeaturesResult":{"properties":{"available":{"type":"boolean","title":"Available"},"download_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Download Path","description":"Relative path for use with GET /projects/{id}/files/download."},"columns":{"items":{"type":"string"},"type":"array","title":"Columns","description":"Feature columns in the CSV (MRID column excluded)."},"row_count":{"type":"integer","title":"Row Count","default":0},"label_map":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/LabelInfo"},"type":"object"},{"type":"null"}],"title":"Label Map","description":"Maps each feature column to its segmentation label information. Columns not present in the label map have no segmentation correspondence. Present only when a label_map resource is declared in the pipeline YAML."},"column_units":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Column Units","description":"Maps each feature column name to its unit string (e.g. 'mm³', 'years', 'a.u.'). Populated from ``default_unit`` / ``column_units`` in the pipeline YAML. For segmentation pipelines the unit is also present on each ``label_map`` entry. Null when no units are declared for this pipeline."}},"type":"object","required":["available"],"title":"BatchFeaturesResult","description":"Summary of the pipeline's batch-level feature CSV."},"Body_upload_bids_files_projects__project_id__files_upload_bids_files_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files"}},"type":"object","required":["files"],"title":"Body_upload_bids_files_projects__project_id__files_upload_bids_files_post"},"Body_upload_bids_projects__project_id__files_upload_bids_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_bids_projects__project_id__files_upload_bids_post"},"Body_upload_csv_projects__project_id__files_upload_csv_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_csv_projects__project_id__files_upload_csv_post"},"Body_upload_dicom_files_projects__project_id__files_dicom_upload_files_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files"}},"type":"object","required":["files"],"title":"Body_upload_dicom_files_projects__project_id__files_dicom_upload_files_post"},"Body_upload_dicom_projects__project_id__files_dicom_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_dicom_projects__project_id__files_dicom_upload_post"},"Body_upload_idat_files_projects__project_id__files_upload_idat_files_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files"}},"type":"object","required":["files"],"title":"Body_upload_idat_files_projects__project_id__files_upload_idat_files_post"},"Body_upload_idat_projects__project_id__files_upload_idat_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_idat_projects__project_id__files_upload_idat_post"},"Body_upload_nifti_projects__project_id__files_upload_nifti_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files"}},"type":"object","required":["files"],"title":"Body_upload_nifti_projects__project_id__files_upload_nifti_post"},"Body_upload_nifti_zip_projects__project_id__files_upload_nifti_zip_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_nifti_zip_projects__project_id__files_upload_nifti_zip_post"},"CentileFeatureMetadataResponse":{"properties":{"features":{"additionalProperties":{"$ref":"#/components/schemas/FeatureDisplayMeta"},"type":"object","title":"Features","description":"Per-variable display metadata keyed by variable name (matching VarName in the centile CSVs). Only variables with non-default behaviour are included; variables absent from this map should be treated as visible and enabled."}},"type":"object","required":["features"],"title":"CentileFeatureMetadataResponse","description":"Response from GET /catalog/centiles/feature-metadata."},"ChunkStatus":{"properties":{"chunk_idx":{"type":"integer","title":"Chunk Idx","description":"Zero-based index of this chunk within the step."},"status":{"type":"string","enum":["pending","running","succeeded","failed"],"title":"Status","description":"Execution state of this chunk."},"subjects":{"items":{"type":"string"},"type":"array","title":"Subjects","description":"MRID stems assigned to this chunk."},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Backend job ID for this chunk's container."},"submitted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message if this chunk failed."}},"type":"object","required":["chunk_idx","status"],"title":"ChunkStatus","description":"Status of one parallel subject chunk within a step."},"CloudStatusResponse":{"properties":{"mode":{"type":"string","enum":["cloud","local"],"title":"Mode","description":"Active execution mode for this server instance."},"queue_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Queue Name","description":"Name of the AWS Batch job queue being monitored."},"running_job_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Running Job Count","description":"Number of jobs currently in RUNNING state on the Batch queue."},"pending_job_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pending Job Count","description":"Number of jobs currently in PENDING (queued, not yet running) state."},"estimated_queue_drain_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Queue Drain Seconds","description":"Rough estimate of seconds until the queue is empty, computed as the sum of (time_per_subject_seconds × num_subjects) across all running and pending jobs. Null when no per-job subject counts are available."}},"type":"object","required":["mode"],"title":"CloudStatusResponse","description":"Current cloud service busyness as reported by the AWS Batch queue.\n\nIn local mode all job-count fields are ``null`` and ``mode`` is ``\"local\"``."},"ColumnCheck":{"properties":{"column":{"type":"string","title":"Column","description":"Required column name."},"present":{"type":"boolean","title":"Present","description":"True if this column exists in participants.csv."},"subjects_missing":{"items":{"type":"string"},"type":"array","title":"Subjects Missing","description":"MRIDs of subjects where this column is empty or absent."},"subjects_invalid":{"items":{"type":"string"},"type":"array","title":"Subjects Invalid","description":"MRIDs of subjects where this column's value fails the pipeline's declared schema (wrong type, out of range, or not in allowed categorical values)."}},"type":"object","required":["column","present"],"title":"ColumnCheck","description":"Readiness check for one required column in participants.csv."},"ColumnSpec":{"properties":{"name":{"type":"string","title":"Name","description":"Column name as it must appear in the CSV header."},"type":{"type":"string","enum":["string","int","float","categorical"],"title":"Type","description":"'string' — any non-empty text. 'int' — whole number, optionally bounded by min/max. 'float' — decimal number, optionally bounded by min/max. 'categorical' — must be one of the strings listed in values.","default":"string"},"min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min","description":"Inclusive lower bound for numeric types. Null means no lower bound."},"max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max","description":"Inclusive upper bound for numeric types. Null means no upper bound."},"values":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Values","description":"Exhaustive list of accepted values for categorical columns."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Human-readable description shown in the UI alongside the column input."}},"type":"object","required":["name"],"title":"ColumnSpec","description":"Validation schema for a single required participants.csv column."},"CommittedFile":{"properties":{"mrid":{"type":"string","title":"Mrid"},"modality":{"type":"string","title":"Modality"},"path":{"type":"string","title":"Path","description":"Path relative to the project root where the file was written."}},"type":"object","required":["mrid","modality","path"],"title":"CommittedFile"},"CompleteSetsRequirement":{"properties":{"required_modalities":{"items":{"type":"string"},"type":"array","title":"Required Modalities","description":"Modalities that must all be present for a subject to be considered complete."},"complete_mrids":{"items":{"type":"string"},"type":"array","title":"Complete Mrids","description":"MRIDs that have files in every required modality."},"incomplete_mrids":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object","title":"Incomplete Mrids","description":"MRIDs that are missing at least one modality. Maps MRID → list of modalities it is missing."},"complete_count":{"type":"integer","title":"Complete Count","description":"Number of subjects with a complete set of modalities."},"satisfied":{"type":"boolean","title":"Satisfied","description":"True if at least one subject has a complete set."}},"type":"object","required":["required_modalities","complete_mrids","incomplete_mrids","complete_count","satisfied"],"title":"CompleteSetsRequirement","description":"Cross-modality check: each subject must have files in every required modality."},"CsvRequirement":{"properties":{"required_columns":{"items":{"$ref":"#/components/schemas/ColumnCheck"},"type":"array","title":"Required Columns","description":"Per-column check results."},"total_subjects":{"type":"integer","title":"Total Subjects","description":"Total subjects (rows) in participants.csv."},"satisfied":{"type":"boolean","title":"Satisfied","description":"True if every required column exists and is non-empty for all subjects."}},"type":"object","required":["required_columns","total_subjects","satisfied"],"title":"CsvRequirement","description":"Aggregate readiness check for all required participants.csv columns."},"DicomConvertRequest":{"properties":{"series_mappings":{"items":{"$ref":"#/components/schemas/SeriesMapping"},"type":"array","title":"Series Mappings","description":"One entry per series to convert. Series not listed are ignored."}},"type":"object","required":["series_mappings"],"title":"DicomConvertRequest","description":"Request body for the DICOM conversion endpoint."},"DicomConvertResult":{"properties":{"run_id":{"type":"string","title":"Run Id","description":"Job run ID. Poll /jobs/pipelines/{run_id} for status."}},"type":"object","required":["run_id"],"title":"DicomConvertResult","description":"Response after a conversion job is successfully submitted."},"DicomSeriesListing":{"properties":{"staging_id":{"type":"string","title":"Staging Id"},"series":{"items":{"$ref":"#/components/schemas/SeriesInfo"},"type":"array","title":"Series"}},"type":"object","required":["staging_id","series"],"title":"DicomSeriesListing","description":"All DICOM series detected in a staged upload."},"DicomStagingResult":{"properties":{"staging_id":{"type":"string","title":"Staging Id","description":"Opaque identifier for this DICOM staging area."}},"type":"object","required":["staging_id"],"title":"DicomStagingResult","description":"Response after uploading a DICOM zip."},"DirectoryTree":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/FileEntry"},"type":"array","title":"Entries"}},"type":"object","title":"DirectoryTree","description":"Flat listing of all visible nodes in a project directory."},"DocManifest":{"properties":{"docs_id":{"type":"string","title":"Docs Id","description":"Topic identifier (folder name under resources/docs/)."},"title":{"type":"string","title":"Title","description":"Human-readable topic title."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"One-sentence summary shown in the docs index."},"pipelines":{"items":{"type":"string"},"type":"array","title":"Pipelines","description":"Pipeline IDs covered by this documentation topic. Multiple pipelines (e.g. harmonized and base variants) can share one topic."},"thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail","description":"Filename of a thumbnail image relative to the topic folder. Fetch with GET /catalog/docs/{docs_id}/{thumbnail}."},"sections":{"items":{"$ref":"#/components/schemas/DocSection"},"type":"array","title":"Sections","description":"Ordered list of content sections available for this topic."}},"type":"object","required":["docs_id","title"],"title":"DocManifest","description":"Full documentation manifest for a single topic."},"DocSection":{"properties":{"id":{"type":"string","title":"Id","description":"Unique identifier for this section within the topic."},"title":{"type":"string","title":"Title","description":"Display title for this section (e.g. tab label)."},"file":{"type":"string","title":"File","description":"Filename relative to the topic folder. Fetch the content with GET /catalog/docs/{docs_id}/{file}."},"audience":{"type":"string","enum":["user","developer","all"],"title":"Audience","description":"'user' — shown in the NiChart end-user interface. 'developer' — shown in developer/technical documentation. 'all' — shown in both contexts.","default":"all"},"type":{"type":"string","enum":["markdown","data","image"],"title":"Type","description":"'markdown' — render as prose with react-markdown or equivalent. 'data' — parse as JSON for plot/chart rendering. 'image' — display as a static image.","default":"markdown"}},"type":"object","required":["id","title","file"],"title":"DocSection","description":"A single section within a documentation topic."},"DocTopicSummary":{"properties":{"docs_id":{"type":"string","title":"Docs Id","description":"Topic identifier."},"title":{"type":"string","title":"Title","description":"Human-readable topic title."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"pipelines":{"items":{"type":"string"},"type":"array","title":"Pipelines","description":"Pipeline IDs covered by this topic."},"thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail","description":"Filename of a thumbnail image relative to the topic folder. Fetch with GET /catalog/docs/{docs_id}/{thumbnail}."}},"type":"object","required":["docs_id","title"],"title":"DocTopicSummary","description":"Abbreviated documentation topic for the index listing."},"DownloadZipRequest":{"properties":{"paths":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Paths","description":"Paths relative to the project root — a mix of files and directories is allowed. Each entry keeps its project-relative path inside the resulting archive, so the original directory structure is preserved."}},"type":"object","required":["paths"],"title":"DownloadZipRequest","description":"Request body for bundling multiple files/directories into a single zip."},"ErrorDetail":{"properties":{"detail":{"type":"string","title":"Detail","description":"Human-readable description of the error."}},"type":"object","required":["detail"],"title":"ErrorDetail","description":"Standard error response body."},"FeatureDisplayMeta":{"properties":{"hidden":{"type":"boolean","title":"Hidden","description":"When True, the variable is excluded from the selector entirely.","default":false},"disabled":{"type":"boolean","title":"Disabled","description":"When True, the variable is shown in the selector but cannot be selected.","default":false},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Display name override. When null the variable name is used as-is."},"group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group","description":"Logical grouping name. The UI may use this to render nested/categorised selectors."}},"type":"object","title":"FeatureDisplayMeta","description":"Display metadata for a single centile variable."},"FeatureGroup":{"properties":{"name":{"type":"string","title":"Name","description":"Display name for this group (e.g. 'Lobar', 'Global')."},"columns":{"items":{"type":"string"},"type":"array","title":"Columns","description":"Feature column names belonging to this group."}},"type":"object","required":["name","columns"],"title":"FeatureGroup","description":"A named group of feature columns for hierarchical display in the UI."},"FileEntry":{"properties":{"name":{"type":"string","title":"Name","description":"Filename or directory name."},"path":{"type":"string","title":"Path","description":"Path relative to the project root."},"type":{"type":"string","enum":["file","directory"],"title":"Type","description":"Node kind."},"size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size","description":"File size in bytes (files only)."},"mtime":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Mtime","description":"Last-modified UNIX timestamp."}},"type":"object","required":["name","path","type"],"title":"FileEntry","description":"A single node in the project file tree."},"FinishedRunsResponse":{"properties":{"runs":{"items":{"$ref":"#/components/schemas/PipelineRunSummary"},"type":"array","title":"Runs","description":"Pipeline runs that reached a terminal state (succeeded or failed) since the last call to this endpoint for this user. Empty when no runs have finished since the last poll."},"polled_at":{"type":"string","format":"date-time","title":"Polled At","description":"Server timestamp when this response was generated. Stored server-side as the cursor for the next call — only runs finishing after this moment appear in the next response."}},"type":"object","required":["runs","polled_at"],"title":"FinishedRunsResponse","description":"Response from the finished-runs polling endpoint."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IOField":{"properties":{"type":{"type":"string","enum":["file","directory"],"title":"Type","description":"Whether this slot is a single file or a directory."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Human-readable description of this slot."},"merge":{"anyOf":[{"type":"string","enum":["directory_union","directory_union_csv_concat","csv_concat"]},{"type":"null"}],"title":"Merge","description":"Output merge strategy used when the tool runs in parallel chunks. Omit (or null) for input slots. 'directory_union': copy all files from each chunk output into the final directory — NIfTI filenames must be unique across chunks (MRID-keyed). 'directory_union_csv_concat': same as directory_union, but CSV files with matching names across chunks are row-concatenated (header kept once). 'csv_concat': concatenate a single CSV file output."}},"type":"object","required":["type"],"title":"IOField","description":"An input or output slot on a tool."},"IdatRequirement":{"properties":{"complete_mrids":{"items":{"type":"string"},"type":"array","title":"Complete Mrids","description":"MRIDs with both _Red.idat and _Grn.idat present."},"missing_red":{"items":{"type":"string"},"type":"array","title":"Missing Red","description":"MRIDs that have _Grn.idat but are missing _Red.idat."},"missing_grn":{"items":{"type":"string"},"type":"array","title":"Missing Grn","description":"MRIDs that have _Red.idat but are missing _Grn.idat."},"complete_count":{"type":"integer","title":"Complete Count","description":"Number of MRIDs with both files present."},"satisfied":{"type":"boolean","title":"Satisfied","description":"True if at least one complete MRID pair exists."}},"type":"object","required":["complete_mrids","missing_red","missing_grn","complete_count","satisfied"],"title":"IdatRequirement","description":"Readiness check for paired IDAT files ({MRID}_Red.idat + {MRID}_Grn.idat)."},"ImagingRequirement":{"properties":{"modality":{"type":"string","title":"Modality","description":"Imaging modality directory (t1, fl, t2, t1ce, adc)."},"subject_count":{"type":"integer","title":"Subject Count","description":"Number of NIfTI files found in the modality directory."},"mrids":{"items":{"type":"string"},"type":"array","title":"Mrids","description":"MRIDs (filename stems) found in this modality directory."},"satisfied":{"type":"boolean","title":"Satisfied","description":"True if at least one subject's file is present."}},"type":"object","required":["modality","subject_count","satisfied"],"title":"ImagingRequirement","description":"Readiness check for a single required imaging modality."},"LabelInfo":{"properties":{"display_name":{"type":"string","title":"Display Name","description":"Human-readable region name."},"label_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Label Ids","description":"Voxel values in the segmentation NIfTI that together form this region. Null for pipelines that do not produce a segmentation output."},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit","description":"Physical unit for this column's values, e.g. 'mm³', 'years'. Null when no unit is declared for this pipeline."}},"type":"object","required":["display_name"],"title":"LabelInfo","description":"Display metadata for a single feature column.\n\n``label_ids`` is only present for pipelines that produce a segmentation NIfTI.\nWhen present, the values are the voxel intensities in the atlas segmentation that\ntogether form this region and should be used to build ROI overlays.\nFor pipelines without segmentation output, ``label_map`` on ``PipelineDetail``\nwill be null rather than containing ``LabelInfo`` entries with empty label_ids."},"ModalityInfo":{"properties":{"code":{"type":"string","title":"Code","description":"Canonical modality code — also the study subdirectory name and the key used in uploads/requirements."},"label":{"type":"string","title":"Label","description":"Human-readable label, e.g. 'T1-weighted'."},"dir":{"type":"string","title":"Dir","description":"Study subdirectory where images of this modality are stored (${STUDY}/{dir}/)."}},"type":"object","required":["code","label","dir"],"title":"ModalityInfo","description":"An imaging modality the platform recognizes."},"NiftiCommitRequest":{"properties":{"mappings":{"items":{"$ref":"#/components/schemas/NiftiMapping"},"type":"array","title":"Mappings","description":"One entry per staged file; every staged file must be accounted for."}},"type":"object","required":["mappings"],"title":"NiftiCommitRequest","description":"Request body for the NIfTI staging commit endpoint."},"NiftiCommitResult":{"properties":{"committed":{"items":{"$ref":"#/components/schemas/CommittedFile"},"type":"array","title":"Committed"},"skipped":{"items":{"$ref":"#/components/schemas/CommittedFile"},"type":"array","title":"Skipped","description":"Entries whose target file already existed and were left untouched instead of being overwritten.","default":[]}},"type":"object","required":["committed"],"title":"NiftiCommitResult","description":"Response after a successful NIfTI commit."},"NiftiMapping":{"properties":{"filename":{"type":"string","title":"Filename","description":"Filename as returned in the staging proposals."},"mrid":{"type":"string","title":"Mrid","description":"Subject identifier. Becomes the file stem in the target directory."},"modality":{"type":"string","title":"Modality","description":"NiChart modality code (see GET /catalog/modalities). Determines which subdirectory the file lands in."}},"type":"object","required":["filename","mrid","modality"],"title":"NiftiMapping","description":"Confirmed mapping for a single staged NIfTI file."},"NiftiStagingResult":{"properties":{"staging_id":{"type":"string","title":"Staging Id","description":"Opaque staging area identifier. Pass to the commit endpoint."},"proposals":{"items":{"$ref":"#/components/schemas/NiftiUploadProposal"},"type":"array","title":"Proposals","description":"Server's best-effort mapping of filenames to MRID and modality."},"skipped_duplicates":{"items":{"type":"string"},"type":"array","title":"Skipped Duplicates","description":"Original relative paths that were skipped because they flattened to the same filename as an earlier entry in this upload (first occurrence wins).","default":[]}},"type":"object","required":["staging_id","proposals"],"title":"NiftiStagingResult","description":"Response after uploading NIfTI file(s); requires a follow-up commit call."},"NiftiUploadProposal":{"properties":{"filename":{"type":"string","title":"Filename","description":"Original uploaded filename."},"inferred_mrid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inferred Mrid","description":"MRID inferred from the filename by stripping known suffixes."},"inferred_modality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inferred Modality","description":"Modality inferred from the filename (see GET /catalog/modalities), or null if not detectable."}},"type":"object","required":["filename"],"title":"NiftiUploadProposal","description":"Server-inferred metadata for a single uploaded NIfTI file."},"ParameterSpec":{"properties":{"type":{"type":"string","title":"Type","description":"Python type name: 'int', 'float', 'bool', or 'str'."},"default":{"anyOf":[{},{"type":"null"}],"title":"Default","description":"Default value if not supplied by the caller."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Human-readable description for UI rendering."},"choices":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Choices","description":"Exhaustive list of allowed values, if constrained."},"min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min","description":"Minimum value (numeric types only)."},"max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max","description":"Maximum value (numeric types only)."}},"type":"object","required":["type"],"title":"ParameterSpec","description":"Specification for a single configurable parameter."},"ParticipantRow":{"properties":{"MRID":{"type":"string","title":"Mrid","description":"Subject identifier. Must match the stem of uploaded NIfTI files."}},"additionalProperties":true,"type":"object","required":["MRID"],"title":"ParticipantRow","description":"One row from participants.csv.\n\n``MRID`` is required. Any additional columns (Age, Sex, MMSE, etc.) are\npreserved as extra fields and round-trip through the API unchanged."},"ParticipantsList":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/ParticipantRow"},"type":"array","title":"Rows"}},"type":"object","title":"ParticipantsList","description":"List of participant rows."},"ParticipantsUpdate":{"properties":{"rows":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Rows","description":"Complete replacement list. Each row must contain an 'MRID' key. Any additional keys are written as extra columns in participants.csv. Existing participants.csv is overwritten."}},"type":"object","required":["rows"],"title":"ParticipantsUpdate","description":"Request body to replace the participants list."},"PerSubjectFileStatus":{"properties":{"available":{"type":"boolean","title":"Available"},"download_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Download Path","description":"Relative path for use with GET /projects/{id}/files/download."}},"type":"object","required":["available"],"title":"PerSubjectFileStatus","description":"Availability of a single per-subject output file."},"PerSubjectOutput":{"properties":{"id":{"type":"string","title":"Id","description":"Output identifier as declared in the pipeline results spec."},"type":{"type":"string","title":"Type","description":"Output type, e.g. 'segmentation_nifti'."},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Human-readable label for this overlay, e.g. 'DLMUSE segmentation'. Use as a subtitle in the MRI panel (fall back to id when absent)."},"subjects":{"additionalProperties":{"$ref":"#/components/schemas/PerSubjectFileStatus"},"type":"object","title":"Subjects","description":"Map of MRID → file availability and download path."}},"type":"object","required":["id","type","subjects"],"title":"PerSubjectOutput","description":"A named per-subject output type (e.g. a segmentation NIfTI)."},"PipelineDetail":{"properties":{"id":{"type":"string","title":"Id","description":"Pipeline identifier (YAML basename without extension)."},"name":{"type":"string","title":"Name","description":"Human-readable pipeline name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"categories":{"items":{"type":"string"},"type":"array","title":"Categories"},"requires":{"items":{"type":"string"},"type":"array","title":"Requires","description":"Data prerequisites (e.g. 'needs_T1', 'needs_demographics')."},"is_harmonized":{"type":"boolean","title":"Is Harmonized","description":"True if this pipeline applies harmonization to its inputs.","default":false},"harmonized_variant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Harmonized Variant","description":"Pipeline ID of the harmonized version of this pipeline. Present on base pipelines only; null on harmonized pipelines and those with no harmonized counterpart. Use to render a 'Switch to harmonized' action."},"base_variant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Variant","description":"Pipeline ID of the standard (non-harmonized) version of this pipeline. Present on harmonized pipelines only; null on base pipelines and those with no base counterpart. Use to render a 'Switch to standard' action."},"docs_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docs Id","description":"Documentation topic identifier for this pipeline. Use with GET /catalog/docs/{docs_id} to retrieve the manifest, and GET /catalog/docs/{docs_id}/{file} to fetch individual sections. Multiple pipelines may share the same docs_id (e.g. harmonized variants)."},"root_pipeline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Root Pipeline","description":"Grouping key for this pipeline, used to cluster related variants (e.g. base/harmonized/CVM flavours) under a single entry in the UI. Pipelines sharing the same root_pipeline belong to the same group; null if the pipeline YAML does not declare one."},"harmonized":{"type":"boolean","title":"Harmonized","description":"True if this pipeline applies harmonization to its inputs. Explicit counterpart to the 'harmonized' category tag, for callers that want to filter/group variants without scanning categories.","default":false},"modalities":{"items":{"type":"string"},"type":"array","title":"Modalities","description":"Imaging/data modalities this pipeline requires (e.g. ['T1'], ['T1', 'FLAIR'], ['PET'], ['idat']), derived from its 'needs_*' prerequisites. Empty for pipelines with no modality requirement (e.g. the test pipeline)."},"steps":{"items":{"$ref":"#/components/schemas/PipelineStep"},"type":"array","title":"Steps"},"parameters":{"additionalProperties":{"$ref":"#/components/schemas/ParameterSpec"},"type":"object","title":"Parameters","description":"User-overridable parameters for this pipeline. Each entry describes the type, default, and optional constraints. Pass values via ``params`` in the pipeline submit body. Step-level params in the YAML are fixed by the pipeline author and cannot be overridden."},"atlas_resource_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Atlas Resource Path","description":"Resource path for the brain atlas NIfTI declared by this pipeline. Fetch with GET /catalog/resources/{path}. Null if no atlas is declared or the file is not present on the server."},"atlas_segmentation_resource_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Atlas Segmentation Resource Path","description":"Resource path for the atlas segmentation NIfTI declared by this pipeline. Fetch with GET /catalog/resources/{path}. Null if no atlas segmentation is declared or the file is not present."},"label_map":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/LabelInfo"},"type":"object"},{"type":"null"}],"title":"Label Map","description":"Maps each batch-feature column name to its display name and, when the pipeline produces a segmentation, the constituent voxel label IDs in the atlas segmentation NIfTI. Null if no label_map resource is configured or the resource file is absent. Pipelines without segmentation output will have this field null."},"feature_groups":{"anyOf":[{"items":{"$ref":"#/components/schemas/FeatureGroup"},"type":"array"},{"type":"null"}],"title":"Feature Groups","description":"Ordered grouping of batch-feature columns for hierarchical UI display (e.g. nested dropdowns). Null if the pipeline does not declare feature_groups in its YAML."},"column_schemas":{"additionalProperties":{"$ref":"#/components/schemas/ColumnSpec"},"type":"object","title":"Column Schemas","description":"Validation schema for each column declared in csv_has_columns. Keyed by column name. Use this to drive client-side CSV validation: type checking, numeric range enforcement, and categorical value lists. Columns not listed here have no declared schema (accept any non-empty value)."}},"type":"object","required":["id","name"],"title":"PipelineDetail","description":"Full pipeline definition including ordered steps and user-configurable parameters."},"PipelineResultDetail":{"properties":{"pipeline_id":{"type":"string","title":"Pipeline Id"},"pipeline_name":{"type":"string","title":"Pipeline Name"},"batch_features":{"anyOf":[{"$ref":"#/components/schemas/BatchFeaturesResult"},{"type":"null"}]},"per_subject":{"items":{"$ref":"#/components/schemas/PerSubjectOutput"},"type":"array","title":"Per Subject"},"atlas_resource_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Atlas Resource Path","description":"Resource path for the brain atlas NIfTI. Fetch with GET /catalog/resources/{path}."},"atlas_segmentation_resource_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Atlas Segmentation Resource Path","description":"Resource path for the atlas segmentation NIfTI (reference-mode overlay when no subject data is selected). Fetch with GET /catalog/resources/{path}."},"subjects":{"additionalProperties":{"$ref":"#/components/schemas/SubjectCompleteness"},"type":"object","title":"Subjects","description":"Per-subject completeness across all declared per_subject outputs."}},"type":"object","required":["pipeline_id","pipeline_name"],"title":"PipelineResultDetail","description":"Full result detail for one pipeline within a project."},"PipelineResultSummary":{"properties":{"pipeline_id":{"type":"string","title":"Pipeline Id"},"pipeline_name":{"type":"string","title":"Pipeline Name"},"has_batch_features":{"type":"boolean","title":"Has Batch Features","description":"True if the batch feature CSV exists in the project."},"per_subject_ids":{"items":{"type":"string"},"type":"array","title":"Per Subject Ids","description":"IDs of declared per-subject output types."},"has_atlas":{"type":"boolean","title":"Has Atlas","description":"True if the atlas resource file is present on the server."}},"type":"object","required":["pipeline_id","pipeline_name","has_batch_features","per_subject_ids","has_atlas"],"title":"PipelineResultSummary","description":"Quick summary of a pipeline's result availability within a project."},"PipelineRunDetail":{"properties":{"run_id":{"type":"string","title":"Run Id","description":"Unique run identifier (UUID)."},"project_id":{"type":"string","title":"Project Id"},"pipeline_id":{"type":"string","title":"Pipeline Id"},"status":{"type":"string","enum":["pending","running","succeeded","failed"],"title":"Status","description":"Overall pipeline status."},"submitted_at":{"type":"string","format":"date-time","title":"Submitted At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"},"current_step":{"type":"integer","title":"Current Step","description":"Index of the step currently executing (0-based).","default":0},"total_steps":{"type":"integer","title":"Total Steps","default":0},"steps":{"items":{"$ref":"#/components/schemas/StepStatus"},"type":"array","title":"Steps"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Top-level error message if the run failed."},"jobs_ahead":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Jobs Ahead","description":"Number of Batch jobs submitted before this one that are still waiting for compute capacity. Cloud mode only; null in local mode or when no step is currently pending on the Batch queue."},"estimated_wait_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Wait Seconds","description":"Estimated seconds until this job reaches the front of the queue, computed as the sum of (num_subjects × time_per_subject_seconds) for each job submitted ahead of this one. Null when timing data is unavailable for any ahead job."}},"type":"object","required":["run_id","project_id","pipeline_id","status","submitted_at"],"title":"PipelineRunDetail","description":"Full pipeline run record including per-step breakdown."},"PipelineRunLogs":{"properties":{"run_id":{"type":"string","title":"Run Id"},"logs":{"type":"string","title":"Logs","description":"Concatenated log output from all steps executed so far."}},"type":"object","required":["run_id","logs"],"title":"PipelineRunLogs","description":"Aggregated logs for all completed steps in a pipeline run."},"PipelineRunSubmit":{"properties":{"pipeline_id":{"type":"string","title":"Pipeline Id","description":"Pipeline identifier (matches a YAML basename in resources/pipelines/)."},"params":{"additionalProperties":true,"type":"object","title":"Params","description":"Parameter overrides applied to every step that accepts them."},"reuse_cached_steps":{"type":"boolean","title":"Reuse Cached Steps","description":"When True, steps whose inputs haven't changed since the last successful run are skipped. Set to False to force a full re-run.","default":true}},"type":"object","required":["pipeline_id"],"title":"PipelineRunSubmit","description":"Request body to submit a pipeline run."},"PipelineRunSummary":{"properties":{"run_id":{"type":"string","title":"Run Id","description":"Unique run identifier (UUID)."},"project_id":{"type":"string","title":"Project Id"},"pipeline_id":{"type":"string","title":"Pipeline Id"},"status":{"type":"string","enum":["pending","running","succeeded","failed"],"title":"Status","description":"Overall pipeline status."},"submitted_at":{"type":"string","format":"date-time","title":"Submitted At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"},"current_step":{"type":"integer","title":"Current Step","description":"Index of the step currently executing (0-based).","default":0},"total_steps":{"type":"integer","title":"Total Steps","default":0}},"type":"object","required":["run_id","project_id","pipeline_id","status","submitted_at"],"title":"PipelineRunSummary","description":"Abbreviated pipeline run record for list responses."},"PipelineStep":{"properties":{"id":{"type":"string","title":"Id","description":"Step identifier, unique within the pipeline."},"tool":{"type":"string","title":"Tool","description":"Tool ID this step invokes."},"inputs":{"additionalProperties":{"type":"string"},"type":"object","title":"Inputs","description":"Input slot → path template mapping."},"outputs":{"additionalProperties":{"type":"string"},"type":"object","title":"Outputs","description":"Output slot → path template mapping."},"params":{"additionalProperties":true,"type":"object","title":"Params","description":"Parameter overrides for this step."}},"type":"object","required":["id","tool","inputs","outputs"],"title":"PipelineStep","description":"A single step within a pipeline definition."},"PipelineSummary":{"properties":{"id":{"type":"string","title":"Id","description":"Pipeline identifier (YAML basename without extension)."},"name":{"type":"string","title":"Name","description":"Human-readable pipeline name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"categories":{"items":{"type":"string"},"type":"array","title":"Categories"},"requires":{"items":{"type":"string"},"type":"array","title":"Requires","description":"Data prerequisites (e.g. 'needs_T1', 'needs_demographics')."},"is_harmonized":{"type":"boolean","title":"Is Harmonized","description":"True if this pipeline applies harmonization to its inputs.","default":false},"harmonized_variant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Harmonized Variant","description":"Pipeline ID of the harmonized version of this pipeline. Present on base pipelines only; null on harmonized pipelines and those with no harmonized counterpart. Use to render a 'Switch to harmonized' action."},"base_variant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Variant","description":"Pipeline ID of the standard (non-harmonized) version of this pipeline. Present on harmonized pipelines only; null on base pipelines and those with no base counterpart. Use to render a 'Switch to standard' action."},"docs_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docs Id","description":"Documentation topic identifier for this pipeline. Use with GET /catalog/docs/{docs_id} to retrieve the manifest, and GET /catalog/docs/{docs_id}/{file} to fetch individual sections. Multiple pipelines may share the same docs_id (e.g. harmonized variants)."},"root_pipeline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Root Pipeline","description":"Grouping key for this pipeline, used to cluster related variants (e.g. base/harmonized/CVM flavours) under a single entry in the UI. Pipelines sharing the same root_pipeline belong to the same group; null if the pipeline YAML does not declare one."},"harmonized":{"type":"boolean","title":"Harmonized","description":"True if this pipeline applies harmonization to its inputs. Explicit counterpart to the 'harmonized' category tag, for callers that want to filter/group variants without scanning categories.","default":false},"modalities":{"items":{"type":"string"},"type":"array","title":"Modalities","description":"Imaging/data modalities this pipeline requires (e.g. ['T1'], ['T1', 'FLAIR'], ['PET'], ['idat']), derived from its 'needs_*' prerequisites. Empty for pipelines with no modality requirement (e.g. the test pipeline)."}},"type":"object","required":["id","name"],"title":"PipelineSummary","description":"Abbreviated pipeline information for list responses."},"Project":{"properties":{"id":{"type":"string","title":"Id","description":"Project identifier (same as directory name)."},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"Creation timestamp (derived from directory mtime)."}},"type":"object","required":["id"],"title":"Project","description":"A user project."},"ProjectCreate":{"properties":{"name":{"type":"string","title":"Name","description":"Project name. Used directly as the directory name and URL path segment. Must match ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$."}},"type":"object","required":["name"],"title":"ProjectCreate","description":"Request body for creating a new project."},"ProvenanceEntry":{"properties":{"output_dir":{"type":"string","title":"Output Dir","description":"Project-relative path of the directory containing this _provenance.json."},"pipeline_id":{"type":"string","title":"Pipeline Id","description":"Pipeline that produced this output."},"step_id":{"type":"string","title":"Step Id","description":"Step within that pipeline."},"container_image":{"type":"string","title":"Container Image","description":"Container image used."},"generated_at":{"type":"string","title":"Generated At","description":"ISO timestamp when the step finished."},"execution_mode":{"type":"string","title":"Execution Mode","description":"Execution mode at time of run: 'local' or 'cloud'.","default":""},"user_id":{"type":"string","title":"User Id","description":"Cognito sub (cloud) or local user identifier of the user who ran the step.","default":""},"backend":{"type":"string","title":"Backend","description":"Job backend used: 'docker', 'singularity', 'slurm', or 'batch'.","default":""},"inputs":{"items":{"$ref":"#/components/schemas/ProvenanceInputCheck"},"type":"array","title":"Inputs","description":"Staleness check for each input path recorded at run time."},"overall":{"type":"string","enum":["clean","dirty","missing_inputs","unreadable"],"title":"Overall","description":"'clean' — all inputs unchanged since the step ran. 'dirty' — at least one input was modified after the step. 'missing_inputs' — at least one input path no longer exists. 'unreadable' — the _provenance.json file could not be parsed."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Parse error message, present only when overall='unreadable'."}},"type":"object","required":["output_dir","pipeline_id","step_id","container_image","generated_at","inputs","overall"],"title":"ProvenanceEntry","description":"Parsed and verified contents of one _provenance.json file."},"ProvenanceInputCheck":{"properties":{"label":{"type":"string","title":"Label","description":"Mount label as declared in the tool YAML."},"path":{"type":"string","title":"Path","description":"Absolute host path recorded at run time."},"status":{"type":"string","enum":["clean","modified","missing"],"title":"Status","description":"'clean' — no files changed after the step finished. 'modified' — at least one file was written after the step's generated_at timestamp. 'missing' — the path no longer exists."},"modified_count":{"type":"integer","title":"Modified Count","description":"Number of files within the path that have been modified. Zero for 'clean' or 'missing' entries.","default":0}},"type":"object","required":["label","path","status"],"title":"ProvenanceInputCheck","description":"Staleness check for one input path recorded in a provenance file."},"ProvenanceReport":{"properties":{"project_id":{"type":"string","title":"Project Id","description":"Project that was scanned."},"entries":{"items":{"$ref":"#/components/schemas/ProvenanceEntry"},"type":"array","title":"Entries","description":"One entry per _provenance.json file found in the project tree."},"summary":{"type":"string","enum":["all_clean","some_dirty","no_provenance"],"title":"Summary","description":"'all_clean' — every step's inputs are unchanged. 'some_dirty' — at least one step has stale or missing inputs. 'no_provenance' — no _provenance.json files found (no steps have completed yet)."}},"type":"object","required":["project_id","entries","summary"],"title":"ProvenanceReport","description":"Provenance verification report for a project."},"ReadinessReport":{"properties":{"pipeline_id":{"type":"string","title":"Pipeline Id","description":"Pipeline that was checked."},"satisfied":{"type":"boolean","title":"Satisfied","description":"True if all hard requirements pass."},"imaging":{"items":{"$ref":"#/components/schemas/ImagingRequirement"},"type":"array","title":"Imaging","description":"One entry per imaging modality required by the pipeline."},"complete_sets":{"anyOf":[{"$ref":"#/components/schemas/CompleteSetsRequirement"},{"type":"null"}],"description":"Cross-modality completeness check. Present when the pipeline requires two or more imaging modalities. Reports which subjects have a full complement of required images."},"idat":{"anyOf":[{"$ref":"#/components/schemas/IdatRequirement"},{"type":"null"}],"description":"IDAT paired-file check. Present when the pipeline has a needs_idat requirement."},"csv":{"anyOf":[{"$ref":"#/components/schemas/CsvRequirement"},{"type":"null"}],"description":"CSV column checks, present only when the pipeline has csv_has_columns requirements."},"subject_count":{"anyOf":[{"$ref":"#/components/schemas/SubjectCountRequirement"},{"type":"null"}],"description":"Subject count check, present only for pipelines with min_subjects requirements (e.g. harmonized pipelines). satisfied=False blocks running; recommended_met=False should surface a warning to the user."}},"type":"object","required":["pipeline_id","satisfied"],"title":"ReadinessReport","description":"Project readiness check result for a specific pipeline."},"ResourceSpec":{"properties":{"vcpus":{"type":"integer","title":"Vcpus","description":"Number of virtual CPUs."},"memory":{"type":"integer","title":"Memory","description":"Memory in MiB."},"gpus":{"type":"integer","title":"Gpus","description":"Number of GPUs required.","default":0}},"type":"object","required":["vcpus","memory"],"title":"ResourceSpec","description":"Compute resources required by a tool."},"RetentionInfo":{"properties":{"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"UTC timestamp when the project will be automatically deleted. Computed as the S3 LastModified of the heartbeat marker object plus PROJECT_RETENTION_DAYS. Refresh by calling POST /projects/{project_id}/retention/refresh."}},"type":"object","required":["expires_at"],"title":"RetentionInfo","description":"Retention status for a project."},"SeriesInfo":{"properties":{"series_uid":{"type":"string","title":"Series Uid","description":"DICOM SeriesInstanceUID."},"series_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Series Description","description":"DICOM SeriesDescription tag."},"modality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modality","description":"DICOM Modality tag (e.g. 'MR', 'CT')."},"study_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Study Date","description":"DICOM StudyDate tag (YYYYMMDD)."},"patient_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Patient Id","description":"DICOM PatientID tag."},"num_files":{"type":"integer","title":"Num Files","description":"Number of DICOM files belonging to this series."}},"type":"object","required":["series_uid","num_files"],"title":"SeriesInfo","description":"Metadata for a single DICOM series detected in a staged upload."},"SeriesMapping":{"properties":{"series_uid":{"type":"string","title":"Series Uid","description":"SeriesInstanceUID of the series to convert."},"nichart_modality":{"type":"string","title":"Nichart Modality","description":"Target NiChart modality code (see GET /catalog/modalities)."},"mrid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mrid","description":"Output filename prefix (MRID). Defaults to the DICOM PatientID if not provided."}},"type":"object","required":["series_uid","nichart_modality"],"title":"SeriesMapping","description":"User-confirmed mapping from a DICOM series to a NiChart modality."},"StepStatus":{"properties":{"step_id":{"type":"string","title":"Step Id","description":"Step identifier within the pipeline definition."},"tool_id":{"type":"string","title":"Tool Id","description":"Tool that this step invokes."},"status":{"type":"string","enum":["pending","running","succeeded","failed","skipped","partially_failed"],"title":"Status","description":"Current execution state. 'partially_failed' means at least one chunk succeeded but at least one failed; the pipeline is halted and partial output is preserved."},"submitted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Backend job ID. For non-parallelized steps only; null when the step runs as chunks. See 'chunks' for per-chunk job IDs."},"container_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Container Image","description":"Container image used for this step."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message if the step failed."},"cached_from_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cached From Run Id","description":"Run ID that originally produced the cached output for this step. Only set when status is 'skipped' and the result was reused from a prior run."},"total_chunks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Chunks","description":"Total number of parallel chunks for this step. Null for non-parallelized steps."},"completed_chunks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Completed Chunks","description":"Number of chunks that have succeeded so far."},"failed_chunks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Failed Chunks","description":"Number of chunks that have failed."},"total_subjects":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Subjects","description":"Total subjects assigned across all chunks."},"subjects_complete":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Subjects Complete","description":"Subjects in all succeeded chunks (guaranteed to have output)."},"chunks":{"items":{"$ref":"#/components/schemas/ChunkStatus"},"type":"array","title":"Chunks","description":"Per-chunk breakdown. Empty for non-parallelized steps."}},"type":"object","required":["step_id","tool_id","status"],"title":"StepStatus","description":"Status record for a single pipeline step."},"SubjectCompleteness":{"properties":{"complete":{"type":"boolean","title":"Complete"},"missing":{"items":{"type":"string"},"type":"array","title":"Missing","description":"IDs of per_subject outputs that are missing for this subject."}},"type":"object","required":["complete","missing"],"title":"SubjectCompleteness","description":"Overall completeness of a subject's pipeline outputs."},"SubjectCountRequirement":{"properties":{"actual":{"type":"integer","title":"Actual","description":"Unique MRIDs detected across all modality directories."},"required":{"type":"integer","title":"Required","description":"Minimum subjects needed to run the pipeline at all."},"recommended":{"type":"integer","title":"Recommended","description":"Recommended number of subjects for reliable results."},"satisfied":{"type":"boolean","title":"Satisfied","description":"True if actual >= required."},"recommended_met":{"type":"boolean","title":"Recommended Met","description":"True if actual >= recommended."}},"type":"object","required":["actual","required","recommended","satisfied","recommended_met"],"title":"SubjectCountRequirement","description":"Readiness check for a minimum subject count, used by harmonized pipelines."},"ToolDetail":{"properties":{"id":{"type":"string","title":"Id","description":"Tool identifier (YAML basename without extension)."},"name":{"type":"string","title":"Name","description":"Human-readable tool name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"inputs":{"additionalProperties":{"$ref":"#/components/schemas/IOField"},"type":"object","title":"Inputs","description":"Named input slots."},"outputs":{"additionalProperties":{"$ref":"#/components/schemas/IOField"},"type":"object","title":"Outputs","description":"Named output slots."},"resources":{"$ref":"#/components/schemas/ResourceSpec"},"parameters":{"additionalProperties":{"$ref":"#/components/schemas/ParameterSpec"},"type":"object","title":"Parameters"},"time_per_subject_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Time Per Subject Seconds","description":"Expected wall-clock seconds to process one subject. Used by GET /cloud/status to estimate queue-drain time."},"parallelizable":{"type":"boolean","title":"Parallelizable","description":"When True, the pipeline orchestrator may split directory inputs into subject chunks and run them as parallel backend jobs, then merge the results.","default":false},"subjects_per_chunk":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Subjects Per Chunk","description":"Default number of subjects per parallel chunk for this tool. Null means use the server global default (10). Only meaningful when parallelizable is True."},"github_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Github Url","description":"Link to the tool's source code repository on GitHub."}},"type":"object","required":["id","name","inputs","outputs","resources"],"title":"ToolDetail","description":"Full tool specification."},"ToolSummary":{"properties":{"id":{"type":"string","title":"Id","description":"Tool identifier (YAML basename without extension)."},"name":{"type":"string","title":"Name","description":"Human-readable tool name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["id","name"],"title":"ToolSummary","description":"Abbreviated tool information for list responses."},"UserInfo":{"properties":{"sub":{"type":"string","title":"Sub"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"groups":{"items":{"type":"string"},"type":"array","title":"Groups","default":[]}},"type":"object","required":["sub"],"title":"UserInfo","description":"Safe user claims returned by GET /auth/me. The raw token is never included."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}