{
  "openapi": "3.1.0",
  "info": {
    "title": "Tamarind Bio API",
    "version": "1.0",
    "description": "The complete Tamarind Bio public API.\n\n- **Jobs, tools, and files** — under `/api`. Submit and manage runs of any tool in the catalog.\n- **Pipelines and molecules** — under `/api/pipelines` and `/api/molecules`. Multi-step pipeline templates/runs, and the molecule store.\n\nEvery request authenticates with an `x-api-key` header."
  },
  "servers": [
    {
      "url": "https://structure-prediction-2mjbuc7x3-tamarind-team.vercel.app",
      "description": "Tamarind API"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/api/submit-job": {
      "post": {
        "summary": "Submit a single job",
        "description": "Submit a job for protein analysis using one of the available tools",
        "operationId": "submitJob",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JobSubmission"
              },
              "example": {
                "jobName": "my-protein-analysis",
                "type": "alphafold",
                "settings": {
                  "sequence": "MKTVRQERLKSIVRILERSKEPVSGAQLAEELSVSRQVIVQDIAYLRSLGYNIVATPRGYVLAGG"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job submitted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/submit-batch": {
      "post": {
        "summary": "Submit multiple jobs as a batch",
        "description": "Submit multiple jobs in a single request for batch processing",
        "operationId": "submitBatch",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchSubmission"
              },
              "example": {
                "batchName": "my-batch-analysis",
                "type": "alphafold",
                "settings": [
                  {
                    "sequence": "QVQLQQSGAELARPGASVKMSCKASGYTFTRYTMHWVKQRPGQGLEWIGYINPSRGYTNYNQKFKDKATLTTDKSSSTAYMQLSSLTSEDSAVYYCARYYDDHYCLDYWGQGTTLTVSS"
                  },
                  {
                    "sequence": "MKTVRQERLKSIVRILERSKEPVSGAQLAEELSVSRQVIVQDIAYLRSLGYNIVATPRGYVLAGG"
                  }
                ],
                "jobNames": [
                  "job1",
                  "job2"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch submitted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid parameters"
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/jobs": {
      "get": {
        "summary": "Get user's jobs",
        "description": "Retrieve a list of jobs for the authenticated user",
        "operationId": "getJobs",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "jobName",
            "in": "query",
            "description": "Return one job given its name",
            "schema": {
              "type": "string",
              "example": "myJobName"
            }
          },
          {
            "name": "batch",
            "in": "query",
            "description": "Return all (sub)jobs in a batch. Note: these report Complete as soon as they finish computing, before the batch's aggregated output is ready. To know when the output is downloadable, fetch the batch's parent with ?jobName=<batchName> and poll its batchStatus field.",
            "schema": {
              "type": "string",
              "example": "myBatchName"
            }
          },
          {
            "name": "startKey",
            "in": "query",
            "description": "Use a previously returned start key to retrieve more than 1000 jobs",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Limit the number of jobs retrieved, if there are additional jobs a startKey will be returned",
            "schema": {
              "type": "integer",
              "default": 1000
            }
          },
          {
            "name": "organization",
            "in": "query",
            "description": "Return all jobs in your organization",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "name": "includeSubjobs",
            "in": "query",
            "description": "Include subjobs from batches in response - only top level jobs are returned by default",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "name": "jobEmail",
            "in": "query",
            "description": "Return jobs for another member in your organization",
            "schema": {
              "type": "string",
              "format": "email",
              "example": "user@email.com"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of jobs",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "jobs": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/JobInfo"
                          }
                        },
                        "startKey": {
                          "type": "string",
                          "description": "Key for pagination to retrieve more jobs"
                        },
                        "statuses": {
                          "type": "object",
                          "properties": {
                            "Complete": {
                              "type": "integer",
                              "description": "Number of completed jobs"
                            },
                            "In Queue": {
                              "type": "integer",
                              "description": "Number of jobs in queue"
                            },
                            "Running": {
                              "type": "integer",
                              "description": "Number of running jobs"
                            },
                            "Stopped": {
                              "type": "integer",
                              "description": "Number of stopped jobs"
                            }
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/JobInfo",
                      "description": "Single job response when jobName parameter is used"
                    }
                  ]
                }
              }
            }
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/result": {
      "post": {
        "summary": "Get job results",
        "description": "Retrieve results for a completed job",
        "operationId": "getResult",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jobName"
                ],
                "properties": {
                  "jobName": {
                    "type": "string",
                    "description": "Name of the job to get results for",
                    "example": "my-protein-analysis"
                  },
                  "jobEmail": {
                    "type": "string",
                    "format": "email",
                    "description": "Email of another member of your team (optional)",
                    "example": "user@email.com"
                  },
                  "fileName": {
                    "type": "string",
                    "description": "Path to a specific file in the job results (optional)",
                    "example": "myfile.txt"
                  },
                  "pdbsOnly": {
                    "type": "boolean",
                    "description": "Return only PDB files (optional)",
                    "example": true
                  },
                  "noAsync": {
                    "type": "boolean",
                    "description": "If true, fail with 400 instead of returning a 202 \"preparing\" response when the aggregated zip is not yet built. Use this if your client cannot poll. Default: false (the server falls back to an async build on miss).",
                    "example": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job results. Returned when the aggregated zip already exists, or when the server was able to build it inline (short-ETA batches: the request is held open for up to ~290s while the batch-aggregate worker finishes, then the signed URL is returned).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string",
                  "description": "S3 presigned URL to download the job results",
                  "example": "https://s3.amazonaws.com/bucket/job-results.zip"
                }
              }
            }
          },
          "202": {
            "description": "The aggregated result zip is not yet built and its estimated build time exceeds the inline-wait budget (~290s), or the wait budget elapsed before the zip was ready. The server has kicked off (or rejoined) a batch-aggregate worker that will build it on demand and upload it to the same S3 key /result would have returned. Poll the batch parent (/jobs?jobName=<batchName>) until its `resultUrl` field appears, then re-call /result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "preparing"
                      ]
                    },
                    "jobName": {
                      "type": "string",
                      "description": "The batch parent's job name (echoed from the request)."
                    },
                    "message": {
                      "type": "string",
                      "description": "Human-readable polling instructions."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          }
        },
        "tags": [
          "Results"
        ]
      }
    },
    "/api/upload/{filename}": {
      "put": {
        "summary": "Upload a file",
        "description": "Upload a file (PDB, sequence, etc.) for use in job submissions",
        "operationId": "uploadFile",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "filename",
            "in": "path",
            "required": true,
            "description": "Name of the file to upload",
            "schema": {
              "type": "string",
              "example": "myfile.pdb"
            }
          },
          {
            "name": "folder",
            "in": "query",
            "description": "Optional folder to upload the file to",
            "schema": {
              "type": "string",
              "example": "myFolder"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary",
                "description": "File content to upload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File uploaded successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "description": "Success message",
                      "example": "File uploaded successfully"
                    },
                    "fileUrl": {
                      "type": "string",
                      "description": "URL of the uploaded file"
                    },
                    "signedUrl": {
                      "type": "string",
                      "description": "Signed URL for accessing the file"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - invalid file"
          },
          "413": {
            "description": "File too large"
          }
        },
        "tags": [
          "Files"
        ]
      }
    },
    "/api/delete-job": {
      "delete": {
        "summary": "Delete a job",
        "description": "Marks a job deleted and hides it from listings; for a batch, its subjobs too. This is a soft delete — result files in storage are not removed. An unknown job name returns 400.",
        "operationId": "deleteJob",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jobName"
                ],
                "properties": {
                  "jobName": {
                    "type": "string",
                    "description": "Name of the job to delete",
                    "example": "myJobName"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Job deleted successfully"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - job not found"
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/delete-file": {
      "delete": {
        "summary": "Delete a file",
        "description": "Delete a file from user account",
        "operationId": "deleteFile",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "filePath",
            "in": "query",
            "description": "Name of the file to delete",
            "schema": {
              "type": "string",
              "example": "path/to/myFileName.txt"
            }
          },
          {
            "name": "folder",
            "in": "query",
            "description": "Path to folder - deletes all files in the specified folder",
            "schema": {
              "type": "string",
              "example": "myFolder"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "File deleted successfully"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - file not found"
          }
        },
        "tags": [
          "Files"
        ]
      }
    },
    "/api/files": {
      "get": {
        "summary": "Get user's files",
        "description": "Retrieve a list of files uploaded by the user",
        "operationId": "getFiles",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of files to return",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of files to skip",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "includeFolders",
            "in": "query",
            "description": "Include folders in the response",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          },
          {
            "name": "folder",
            "in": "query",
            "description": "Path to folder to view files within that folder",
            "schema": {
              "type": "string",
              "example": "myFolder"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of files",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "fileId": {
                            "type": "string",
                            "description": "Unique identifier for the file"
                          },
                          "fileName": {
                            "type": "string",
                            "description": "Original filename"
                          },
                          "fileSize": {
                            "type": "integer",
                            "description": "File size in bytes"
                          },
                          "uploadTime": {
                            "type": "string",
                            "format": "date-time",
                            "description": "When the file was uploaded"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total number of files"
                    },
                    "hasMore": {
                      "type": "boolean",
                      "description": "Whether there are more files available"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Files"
        ]
      }
    },
    "/api/tools": {
      "get": {
        "summary": "List available tools",
        "description": "The tools this account can DISCOVER, each with its settings schema. Scoped to the caller. Fetch this rather than assuming a tool name.\n\nAbsence does not prove a tool is unsubmittable: custom tools deployed on the current platform are runnable, and their schemas are available at `/tools/{name}/schema`, but they are not listed here (see the `custom` parameter).",
        "operationId": "listTools",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "custom",
            "in": "query",
            "description": "Return your organization's custom tools instead of the built-in catalogue.\n\nLists tools from the legacy custom-tool store only. Custom tools deployed on the current platform are submittable, and their schemas are available at `/tools/{name}/schema`, but they are not returned here — if you deploy through the current platform, use the tool name you deployed rather than discovering it through this parameter.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The available tools",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ToolInfo"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid API key"
          }
        },
        "tags": [
          "Tools"
        ]
      }
    },
    "/api/tools/{name}/schema": {
      "get": {
        "summary": "A tool's settings as JSON Schema",
        "description": "The parameters this tool accepts, as a standard JSON Schema document, scoped to your account. Validate a `settings` object against it before submitting. Domain types JSON Schema cannot express (a PDB file, a residue selection) travel as strings and keep their original type under `x-tamarind-type`.\n\nResolved through the same classifier `POST /submit-job` uses, so the schema describes the tool version your submission will actually run. A tool you cannot see and one that does not exist both answer 404.\n\nA custom tool that is mid-deploy (status `In Queue` or `Running`) also answers 404, because `/submit-job` refuses it in that state — the schema is unavailable until its deployment finishes rather than describing a contract you cannot submit.\n\nThis is a STATIC description, for code generation, form building and offline checking. To check a specific payload's FIELDS use `POST /validate-job`, which runs the same validator `/submit-job` does and so cannot disagree with it about field values. Note it validates fields only: it does not re-check org or team tool policy, or whether a custom tool is mid-deploy, so a job can still be refused at submission for those reasons.",
        "operationId": "getToolSchema",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "toolRef",
            "in": "query",
            "description": "Describe a specific pinned build of a custom tool rather than the deployed one — the same `toolRef` accepted by `POST /submit-job`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "The tool's `name`, as returned by `GET /tools`.",
            "schema": {
              "type": "string",
              "example": "alphafold"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON Schema for the tool's settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid API key. The classic surface answers 400 here rather than 401, and this endpoint follows it."
          },
          "404": {
            "description": "No such tool, or not available to this account — a tool you cannot run is reported the same way as one that does not exist."
          }
        },
        "tags": [
          "Tools"
        ]
      }
    },
    "/api/validate-job": {
      "post": {
        "summary": "Validate a job without submitting it",
        "description": "Runs the exact validation `/submit-job` runs, without submitting and at no cost. Returns 200 whether or not the payload is valid — read the `valid` field. On success `normalized` is the payload to submit, with defaults filled in.",
        "operationId": "validateJob",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "settings"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "example": "esmfold"
                  },
                  "settings": {
                    "$ref": "#/components/schemas/JobSubmission/properties/settings"
                  },
                  "jobName": {
                    "type": "string",
                    "description": "Optional — when given, a duplicate name is reported as invalid."
                  },
                  "toolRef": {
                    "type": "string",
                    "description": "Optional. Pin validation to a specific custom-tool build (the same `toolRef` you passed to `/tools/{name}/schema`). Omit it and validation resolves the deployed version, which may declare a different set of settings than the build you fetched the schema for."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The verdict. Note that an invalid payload is also a 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid API key"
          },
          "405": {
            "description": "Method not allowed"
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/stop-job": {
      "post": {
        "summary": "Stop a running or queued job",
        "description": "Stops a job that is Running, In Queue, Pending or Waiting. For a batch, stops every stoppable child and the parent.",
        "operationId": "stopJob",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jobName"
                ],
                "properties": {
                  "jobName": {
                    "type": "string",
                    "example": "my-protein-analysis"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stopped",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "stoppedCount": {
                      "type": "integer",
                      "description": "How many jobs were stopped, including batch children."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid API key, unknown job, or the job is not in a stoppable state"
          },
          "405": {
            "description": "Method not allowed"
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/finetuned-models": {
      "get": {
        "summary": "List your finetuned models",
        "description": "Models you own, plus those shared within your organization.",
        "operationId": "listFinetunedModels",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Filter by finetune type, e.g. plm-finetune.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The available models",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "personalModels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FinetunedModel"
                      }
                    },
                    "organizationModels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FinetunedModel"
                      }
                    },
                    "totalCount": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid type or limit"
          },
          "401": {
            "description": "Missing or invalid credentials"
          }
        },
        "tags": [
          "Models"
        ]
      }
    },
    "/api/usage-statistics": {
      "get": {
        "summary": "Usage statistics",
        "description": "Weighted-hours, hours, or job counts. Organization scope is the default and covers every member; if the caller is not authorized for it, the request is served at user scope instead — read `metadata.scope` to see which was applied.",
        "operationId": "getUsageStatistics",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "statistic",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "hours",
                "weighted_hours",
                "jobs"
              ],
              "default": "hours"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "organization"
              ],
              "default": "organization"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Usage, one entry per member in scope",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "users": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "email": {
                            "type": "string"
                          },
                          "total": {
                            "type": "number"
                          },
                          "tools": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    },
                    "lastUpdated": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "metadata": {
                      "type": "object",
                      "properties": {
                        "statistic": {
                          "type": "string"
                        },
                        "scope": {
                          "type": "string",
                          "description": "The scope actually applied, which may be narrower than requested."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials"
          },
          "403": {
            "description": "Organization membership could not be verified"
          }
        },
        "tags": [
          "Usage"
        ]
      }
    },
    "/api/submit-pipeline": {
      "post": {
        "summary": "Create and run a new pipeline",
        "description": "Defines a multi-stage pipeline inline and submits it. Each stage names a task and the tools to run for it; a stage's outputs feed the next. This is the legacy pipeline API — new integrations should use the pipelines endpoints under `/api/pipelines`, which separate a reusable template from a run.",
        "operationId": "submitPipeline",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jobName",
                  "stages"
                ],
                "properties": {
                  "jobName": {
                    "type": "string",
                    "description": "Name for this pipeline, unique within your account."
                  },
                  "stages": {
                    "type": "array",
                    "minItems": 1,
                    "description": "The stages to run, in order.",
                    "items": {
                      "$ref": "#/components/schemas/PipelineStage"
                    }
                  },
                  "initialInputs": {
                    "type": "array",
                    "minItems": 1,
                    "description": "Inputs fed into the first stage. Required (non-empty) whenever any first-stage setting has the value `\"pipe\"`, which marks the field that each initial input is substituted into; omitting it then is a 400 `Missing initial inputs`. Each entry is a raw sequence, or the name of a file you uploaded — `.pdb`/`.sdf` are passed through as file inputs, and a `.fa`/`.fasta` is expanded server-side into its sequences.",
                    "items": {
                      "type": "string"
                    }
                  },
                  "projectTag": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submitted. Body is the plain-text confirmation `Pipeline {jobName} submitted to queue.`",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid API key, a missing/empty `stages`, a duplicate `jobName`, a stage with no task or tools, an unknown filter metric, or an unsupported tool."
          },
          "403": {
            "description": "A tool in the pipeline is not available to your account"
          },
          "503": {
            "description": "A tool could not be resolved (undeployed, or a transient error) — retry"
          }
        },
        "tags": [
          "Pipelines"
        ]
      }
    },
    "/api/models": {
      "get": {
        "summary": "List your deployed models",
        "description": "Custom models you have deployed, plus those shared within your organization. Deleted models are omitted.",
        "operationId": "listModels",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Return just this model instead of the full list.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deployed models — or, when `name` is given, that single model object.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "title": "Model list",
                      "type": "object",
                      "properties": {
                        "personalModels": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/DeployedModel"
                          }
                        },
                        "organizationModels": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/DeployedModel"
                          }
                        },
                        "allModels": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/DeployedModel"
                          }
                        },
                        "totalCount": {
                          "type": "integer"
                        }
                      }
                    },
                    {
                      "title": "Single model",
                      "description": "Returned when the `name` query parameter is supplied.",
                      "$ref": "#/components/schemas/DeployedModel"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "No model with that name"
          }
        },
        "tags": [
          "Models"
        ]
      }
    },
    "/api/deploy-model": {
      "post": {
        "summary": "Deploy a custom model",
        "description": "Deploys your own code as a tool on Tamarind. Upload the entrypoint script and any environment file first with `PUT /upload/{filename}`, then reference them by filename here. When no `environment` is given the environment is inferred, which is only supported for a `.py` entrypoint.",
        "operationId": "deployModel",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "entrypoint"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Unique model name; may not collide with a built-in tool."
                  },
                  "entrypoint": {
                    "type": "string",
                    "description": "An uploaded script path, or a command. `default` uses the image's own entrypoint."
                  },
                  "environment": {
                    "type": "string",
                    "description": "An uploaded environment file (conda, requirements, Dockerfile). Required unless the entrypoint is a `.py` script."
                  },
                  "fields": {
                    "description": "The settings your model takes, in the same shape as a tool's settings. Accepts the array or its JSON-encoded string form — deploy-model.js does `typeof fields === 'string' ? JSON.parse(fields) : fields`, so existing callers send the encoded string.",
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "description": {
                    "type": "string"
                  },
                  "tags": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string",
                        "description": "JSON-encoded array, accepted for backward compatibility."
                      }
                    ]
                  },
                  "gpu": {
                    "type": "boolean"
                  },
                  "outputs": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string"
                                },
                                "description": {
                                  "type": "string"
                                }
                              }
                            }
                          ]
                        }
                      },
                      {
                        "type": "string",
                        "description": "JSON-encoded array, accepted for backward compatibility."
                      }
                    ]
                  },
                  "outputType": {
                    "type": "string"
                  },
                  "outputDescription": {
                    "type": "string"
                  },
                  "runCommand": {
                    "type": "string"
                  },
                  "dockerImageType": {
                    "type": "string"
                  },
                  "dockerContext": {
                    "type": "string"
                  },
                  "contextZip": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deployed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeployedModel"
                }
              }
            }
          },
          "400": {
            "description": "Missing `name`/`entrypoint`, a name that already exists, a referenced file that was never uploaded, or a missing environment for a non-Python entrypoint."
          },
          "405": {
            "description": "Method not allowed"
          }
        },
        "tags": [
          "Models"
        ]
      }
    },
    "/api/run-pipeline": {
      "post": {
        "summary": "Run a saved pipeline",
        "description": "Runs a saved multi-stage pipeline by name. This is the legacy pipeline API; new integrations should use the pipelines endpoints under `/api/pipelines`.",
        "operationId": "runPipeline",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jobName",
                  "pipelineName",
                  "initialInputs"
                ],
                "properties": {
                  "jobName": {
                    "type": "string",
                    "description": "Name for this pipeline execution."
                  },
                  "pipelineName": {
                    "type": "string"
                  },
                  "version": {
                    "type": "string",
                    "description": "Optional saved version; defaults to the pipeline's default."
                  },
                  "initialInputs": {
                    "type": "array",
                    "minItems": 1,
                    "description": "Uploaded .pdb filenames or raw sequences, matching the pipeline's configured input type. Must be non-empty. Basenames must be unique — child job names are derived from them.",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submitted",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                },
                "example": "Pipeline \"my-pipeline\" execution \"run-01\" submitted to queue with 3 jobs."
              }
            }
          },
          "400": {
            "description": "Missing or invalid API key, or invalid inputs"
          },
          "403": {
            "description": "Denied — a tool in the pipeline is restricted for this account, or a budget cap would be exceeded."
          },
          "404": {
            "description": "Pipeline not found"
          }
        },
        "tags": [
          "Pipelines"
        ]
      }
    },
    "/api/molecules/groups": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "List Groups",
        "description": "List your molecule groups.\n\nA group is a named collection of molecules. Pass `scope=org` to include your whole organization.\n\nPaginated — follow `nextCursor` until it is null.",
        "operationId": "listGroups",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "description": "Case-insensitive substring match on the group's name.",
              "title": "Search"
            },
            "description": "Case-insensitive substring match on the group's name."
          },
          {
            "name": "filter",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "maxItems": 32
                },
                {
                  "type": "null"
                }
              ],
              "title": "Filter"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(mine|org)$",
              "description": "`mine` (the default) shows groups you created; `org` shows every group in your organization.",
              "default": "mine",
              "title": "Scope"
            },
            "description": "`mine` (the default) shows groups you created; `org` shows every group in your organization."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(recent|name|size)$",
              "description": "Sort by `recent` (the default, by creation date), `name`, or `size` (how many molecules the group holds). Pair with `dir`.",
              "default": "recent",
              "title": "Sort"
            },
            "description": "Sort by `recent` (the default, by creation date), `name`, or `size` (how many molecules the group holds). Pair with `dir`."
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(asc|desc)$",
              "description": "Sort direction, `asc` or `desc`.",
              "default": "desc",
              "title": "Dir"
            },
            "description": "Sort direction, `asc` or `desc`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "Maximum number of items to return in one page.",
              "default": 50,
              "title": "Limit"
            },
            "description": "Maximum number of items to return in one page."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 4096
                },
                {
                  "type": "null"
                }
              ],
              "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal).",
              "title": "Cursor"
            },
            "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicGroupPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Create Group",
        "description": "Create an empty molecule group.\n\nAdd molecules with `POST /molecules/upload` (JSON) or `POST /molecules/import-file` (a file).\n\nBind a `schemaId` to require every molecule to match that schema.",
        "operationId": "createGroup",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCreateGroupRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicGroup"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/groups/{group_id}": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Get Group",
        "description": "Fetch one group by id — its name, size, and origin.",
        "operationId": "getGroup",
        "parameters": [
          {
            "name": "group_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Group Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicGroup"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/schemas": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "List Schemas",
        "description": "List your schemas.\n\nPass `scope=org` to include every schema in your organization.",
        "operationId": "listSchemas",
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(mine|org)$",
              "default": "mine",
              "title": "Scope"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "Maximum number of items to return in one page.",
              "default": 50,
              "title": "Limit"
            },
            "description": "Maximum number of items to return in one page."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 4096
                },
                {
                  "type": "null"
                }
              ],
              "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal).",
              "title": "Cursor"
            },
            "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSchemaPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Create Schema",
        "description": "Define a reusable set of typed fields you can bind to a group.\n\nScalar fields (`string`, `integer`, `float`, `boolean`, `category`) constrain a molecule's metadata.\n\nA `chain` field describes a required chain, named by its `name`.",
        "operationId": "createSchema",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCreateSchemaRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSchema"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/schemas/{schema_id}": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Get Schema",
        "description": "Fetch one schema by id.",
        "operationId": "getSchema",
        "parameters": [
          {
            "name": "schema_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Schema Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSchema"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "patch": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Update Schema",
        "description": "Update a schema's name and/or fields.\n\nSending `fields` replaces the whole list.\n\nOnly future uploads are validated; molecules already in bound groups are left as they are.",
        "operationId": "updateSchema",
        "parameters": [
          {
            "name": "schema_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Schema Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpdateSchemaRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicSchema"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/remove": {
      "post": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Remove Molecules",
        "description": "Remove molecules from a group.\n\nThis detaches group membership; it does not delete the molecule, which stays in any other groups with its scores and files intact. To delete a molecule everywhere, use `DELETE /molecules/{moleculeId}`.\n\nIdempotent — ids not in the group are ignored, and `removedIds` lists what was detached.",
        "operationId": "removeMolecules",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicRemoveMoleculesFromGroupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicRemoveMoleculesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/upload": {
      "post": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Upload Molecules",
        "description": "Upload proteins and small molecules to a group, specifying the chains as string values.",
        "operationId": "uploadMolecules",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUploadRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicUploadResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/import-file": {
      "post": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Start a file import",
        "description": "Import molecules from a file. Step 1 of 3.\n\nDeclare the file to get a presigned `uploadUrl`, `PUT` the file to it, then `POST /molecules/imports/{importId}/commit`.\n\nFor a CSV, map chain columns with `chainMapping` and scalar columns with `columnMapping`. For PDB, SDF, FASTA, or zip files, the chains are read from the file.",
        "operationId": "importFile",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicFileImportRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicFileImportStart"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/imports/{import_id}": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Check import status",
        "description": "Check an import's progress after committing.\n\nStatus moves `created` → `uploaded` → `queued` → `ingested` (or `failed`).\n\nIt reflects this import specifically, not the target group's overall state.",
        "operationId": "getImport",
        "parameters": [
          {
            "name": "import_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Import Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicImportStatus"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/imports/{import_id}/commit": {
      "post": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Commit an import",
        "description": "Parse the uploaded file and ingest its molecules into the group. Step 3 of 3.\n\nReturns `status: \"queued\"` immediately — poll `GET /molecules/imports/{importId}`.",
        "operationId": "commitImport",
        "parameters": [
          {
            "name": "import_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Import Id"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/PublicCommitRequest"
                  },
                  {
                    "type": "null"
                  }
                ],
                "title": "Body"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicCommitQueued"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "List Molecules",
        "description": "List your molecules, most recently created first.\n\nEach molecule includes its chains, scores, metadata, and files inline. Pass `scope=org` to list across your whole organization.\n\nSearch by `group`, `jobId`/`jobName`, tool, name, or protein-sequence / SMILES subsequence. Filter or sort by tool scores (e.g. `alphafold.ptm`).\n\nPaginated — follow `nextCursor` until it is null.",
        "operationId": "listMolecules",
        "parameters": [
          {
            "name": "group",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "description": "Limit to one group (by id). A group that isn't yours returns an empty page.",
              "title": "Group"
            },
            "description": "Limit to one group (by id). A group that isn't yours returns an empty page."
          },
          {
            "name": "jobId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "description": "Limit to one job's molecules (produced or consumed). Pass a batch id, not a child job id.",
              "title": "Jobid"
            },
            "description": "Limit to one job's molecules (produced or consumed). Pass a batch id, not a child job id."
          },
          {
            "name": "jobName",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "description": "Same as `jobId`, by job/batch name. Names aren't unique — all visible matches are included.",
              "title": "Jobname"
            },
            "description": "Same as `jobId`, by job/batch name. Names aren't unique — all visible matches are included."
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MoleculeType"
                  }
                },
                {
                  "type": "null"
                }
              ],
              "description": "Limit by molecule kind (e.g. protein, small_molecule, nucleic_acid).",
              "title": "Type"
            },
            "description": "Limit by molecule kind (e.g. protein, small_molecule, nucleic_acid)."
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 10000
                },
                {
                  "type": "null"
                }
              ],
              "description": "Search term. `mode=default` (alias `name`): a case-insensitive substring of a molecule name, metadata key, or score metric (`matchedOn` says which); needs ≥3 characters without a `group`, up to 200. `mode=sequence`: an amino-acid sequence, ≥3 characters, up to 10000 (see `sequenceMatch`).",
              "title": "Search"
            },
            "description": "Search term. `mode=default` (alias `name`): a case-insensitive substring of a molecule name, metadata key, or score metric (`matchedOn` says which); needs ≥3 characters without a `group`, up to 200. `mode=sequence`: an amino-acid sequence, ≥3 characters, up to 10000 (see `sequenceMatch`)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(default|name|sequence)$",
              "description": "How `search` is read. `default` (alias `name`) matches molecule names, metadata keys, and score metrics. `sequence` scans chains org-wide for an amino-acid subsequence — a chunked scan, so follow `nextCursor` (empty pages are normal) and watch `scanProgress` (0.0-1.0).",
              "default": "default",
              "title": "Mode"
            },
            "description": "How `search` is read. `default` (alias `name`) matches molecule names, metadata keys, and score metrics. `sequence` scans chains org-wide for an amino-acid subsequence — a chunked scan, so follow `nextCursor` (empty pages are normal) and watch `scanProgress` (0.0-1.0)."
          },
          {
            "name": "sequenceMatch",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(subsequence|exact)$",
              "description": "With `mode=sequence`, how to match the term: `subsequence` (default) — chains contain it; `exact` — a chain equals it. Rejected in name mode.",
              "default": "subsequence",
              "title": "Sequencematch"
            },
            "description": "With `mode=sequence`, how to match the term: `subsequence` (default) — chains contain it; `exact` — a chain equals it. Rejected in name mode."
          },
          {
            "name": "examplesPerGroup",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer",
                  "maximum": 25,
                  "minimum": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cap how many molecules each group contributes to a page (requires `sortBy=groupName`). Not allowed with `mode=sequence`.",
              "title": "Examplespergroup"
            },
            "description": "Cap how many molecules each group contributes to a page (requires `sortBy=groupName`). Not allowed with `mode=sequence`."
          },
          {
            "name": "sequence",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "description": "Match molecules containing this amino-acid subsequence in any chain (case-insensitive, at least 3 characters).",
              "title": "Sequence"
            },
            "description": "Match molecules containing this amino-acid subsequence in any chain (case-insensitive, at least 3 characters)."
          },
          {
            "name": "filter",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "maxItems": 32
                },
                {
                  "type": "null"
                }
              ],
              "description": "Repeatable `field:operator:value` predicate, AND-ed, on a metadata field or tool score (`tool.metric`). Operators: `gt`, `gte`, `lt`, `lte`, `eq`, `neq`, `exists` (written `field:exists`, no value — metadata fields only). Append `:jobId` to pin a tool score to one job.",
              "title": "Filter"
            },
            "description": "Repeatable `field:operator:value` predicate, AND-ed, on a metadata field or tool score (`tool.metric`). Operators: `gt`, `gte`, `lt`, `lte`, `eq`, `neq`, `exists` (written `field:exists`, no value — metadata fields only). Append `:jobId` to pin a tool score to one job."
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(mine|org)$",
              "description": "`mine` (the default) shows molecules in groups you created; `org` shows everything across your organization.",
              "default": "mine",
              "title": "Scope"
            },
            "description": "`mine` (the default) shows molecules in groups you created; `org` shows everything across your organization."
          },
          {
            "name": "sortBy",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200,
              "description": "Sort by a built-in column (`id`, `added`, `type`, `groupName`) or a metadata field / tool score (`tool.metric`, on the tool's best run). `groupName` keeps a group's rows together — pair with `examplesPerGroup`.",
              "default": "added",
              "title": "Sortby"
            },
            "description": "Sort by a built-in column (`id`, `added`, `type`, `groupName`) or a metadata field / tool score (`tool.metric`, on the tool's best run). `groupName` keeps a group's rows together — pair with `examplesPerGroup`."
          },
          {
            "name": "dir",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(asc|desc)$",
              "description": "Sort direction, `asc` or `desc`.",
              "default": "desc",
              "title": "Dir"
            },
            "description": "Sort direction, `asc` or `desc`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "Maximum number of items to return in one page.",
              "default": 50,
              "title": "Limit"
            },
            "description": "Maximum number of items to return in one page."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 4096
                },
                {
                  "type": "null"
                }
              ],
              "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal).",
              "title": "Cursor"
            },
            "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicMoleculePage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/{molecule_id}/metadata": {
      "patch": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Update Molecule Metadata",
        "description": "Update a molecule. All fields are optional and applied together; `null` clears a field.\n\nMerge your own annotations with `properties` (tool scores aren't editable here). Set the derived-from molecule with `source`, this group's primary structure file with `fileId`, and the per-group display name with `name`.\n\nA `name` already used in the group returns `409`.",
        "operationId": "updateMoleculeMetadata",
        "parameters": [
          {
            "name": "molecule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Molecule Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicUpdateMetadataRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicMolecule"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/{molecule_id}/groups": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "List Molecule Groups",
        "description": "List every group a molecule belongs to.\n\nUse this when `GET /molecules/{moleculeId}` marks `groups` as truncated. Only your own groups are listed.\n\nPaginated — follow `nextCursor` until it is null.",
        "operationId": "listMoleculeGroups",
        "parameters": [
          {
            "name": "molecule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Molecule Id"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "Maximum number of items to return in one page.",
              "default": 50,
              "title": "Limit"
            },
            "description": "Maximum number of items to return in one page."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 4096
                },
                {
                  "type": "null"
                }
              ],
              "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal).",
              "title": "Cursor"
            },
            "description": "Pagination token from the previous response's `nextCursor`. Omit for the first page; follow `nextCursor` until null (an empty `items` page while `nextCursor` is set is normal)."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicGroupPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/molecules/{molecule_id}": {
      "get": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Get Molecule",
        "description": "Get one molecule — its chains, scores, files, provenance, and groups, all inline.\n\nAddressed by its id; no group needed.\n\nChain labels are per-group, so pass `groupId` to choose which group's labels you get, else the most recent group wins.",
        "operationId": "getMolecule",
        "parameters": [
          {
            "name": "molecule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Molecule Id"
            }
          },
          {
            "name": "groupId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "title": "Groupid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicMolecule"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "tags": [
          "molecules-public"
        ],
        "summary": "Delete Molecule",
        "description": "Permanently delete a molecule. Admin only.\n\nRemoves the molecule and all its group memberships, scores, and file links across the organization. To remove it from one group instead, use `POST /molecules/remove`.\n\nIdempotent — an unknown id returns `deleted: false`.",
        "operationId": "deleteMolecule",
        "parameters": [
          {
            "name": "molecule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Molecule Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicDeleteMoleculeResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/pipelines/templates": {
      "post": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Create a pipeline template",
        "description": "Create a pipeline template from a `pipeline` graph of inputs, tools, and filters.\n\n- The graph is validated on create\n- This first save becomes the template's first version; every later save adds a new immutable version",
        "operationId": "createTemplate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicCreateTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicTemplate"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      },
      "get": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "List pipeline templates",
        "description": "List pipeline templates in your account or organization.",
        "operationId": "listTemplates",
        "parameters": [
          {
            "name": "isPublished",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter to published (`true`) or unpublished (`false`) templates.",
              "title": "Ispublished"
            },
            "description": "Filter to published (`true`) or unpublished (`false`) templates."
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "mine",
                    "org"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Whose templates to list: `mine` (the default) or `org` for your whole organization.",
              "title": "Owner"
            },
            "description": "Whose templates to list: `mine` (the default) or `org` for your whole organization."
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "description": "Search pipelines by name.",
              "title": "Search"
            },
            "description": "Search pipelines by name."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "Maximum number of templates to return in one page.",
              "default": 50,
              "title": "Limit"
            },
            "description": "Maximum number of templates to return in one page."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 4096
                },
                {
                  "type": "null"
                }
              ],
              "description": "Pagination token from the previous response's `nextCursor`.",
              "title": "Cursor"
            },
            "description": "Pagination token from the previous response's `nextCursor`."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicTemplatePage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/templates/{template_id}": {
      "get": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Get a pipeline template",
        "description": "Retrieve a template by id to view its nodes and required inputs.",
        "operationId": "getTemplate",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Template Id"
            }
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "A specific version to view (a `vN` handle); omit for the current version.",
              "title": "Version"
            },
            "description": "A specific version to view (a `vN` handle); omit for the current version."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicTemplate"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      },
      "delete": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Delete a pipeline template",
        "description": "Delete a template and all its versions.\n\n- Existing runs keep their own copies and are unaffected",
        "operationId": "deleteTemplate",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Template Id"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/templates/{template_id}/publish": {
      "post": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Publish a template version",
        "description": "Publish a version of your pipeline template to your organization.\n\nOthers in your organization may only run pipelines published to the organization\n\nOnly one version can be published at a time",
        "operationId": "publishTemplate",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Template Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicPublishRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPublishResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/templates/{template_id}/duplicate": {
      "post": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Duplicate a pipeline template",
        "description": "Create a copy of a version of your existing pipeline (latest by default).",
        "operationId": "duplicateTemplate",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Template Id"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/PublicDuplicateTemplateRequest"
                  },
                  {
                    "type": "null"
                  }
                ],
                "title": "Body"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicTemplate"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/templates/{template_id}/validate": {
      "post": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Validate a template",
        "description": "Validate the settings and inputs of a template without creating or executing it.\n\nValidates your pipeline to ensure compatible connections, required tool settings, and valid graph structure.",
        "operationId": "validateTemplate",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Template Id"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/PublicValidateTemplateRequest"
                  },
                  {
                    "type": "null"
                  }
                ],
                "title": "Body"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicValidateResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/submit": {
      "post": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Submit a pipeline run",
        "description": "Submit a pipeline run from an existing template, or from a new pipeline graph.\n\nProvide a `bindings` map, which defines the molecules/files you want to use for each input node in your pipeline. If your input contains chains, define the mapping of chain IDs between your molecule and the pipeline's reference/default (if running against an existing template).\n\nBindings may be defined as an array of sequences, smiles, or sdf/pdb files (uploaded using the /upload endpoint), or an existing molecule group id (created using /molecules/groups below).\n\nIf applicable, you may use the `residuesByChain` setting to define selected residues across tools which require hotspots / designed residues to be specified.\n\nChoose from your pipelines or example templates to view example scripts.",
        "operationId": "submitPipeline",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 255
                },
                {
                  "type": "null"
                }
              ],
              "title": "Idempotency-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicSubmitPipelineRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicRun"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/validate": {
      "post": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Validate a pipeline run",
        "description": "Validate the settings and inputs of a run without creating or executing it. Uses the same settings as `/submit`.\n\nValidates your pipeline to ensure compatible connections, required tool settings, and valid graph structure.",
        "operationId": "validateRun",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicSubmitPipelineRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicValidateResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/runs": {
      "get": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "List pipeline runs",
        "description": "List pipeline runs in your organization.",
        "operationId": "listRuns",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/RunStatus"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by run status.",
              "title": "Status"
            },
            "description": "Filter by run status."
          },
          {
            "name": "templateId",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter to runs of one pipeline (by template id).",
              "title": "Templateid"
            },
            "description": "Filter to runs of one pipeline (by template id)."
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "mine",
                    "org"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Whose runs to list: `mine` (the default) or `org` for your whole organization.",
              "title": "Owner"
            },
            "description": "Whose runs to list: `mine` (the default) or `org` for your whole organization."
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "test",
                    "production"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by run source: `test` or `production`.",
              "title": "Source"
            },
            "description": "Filter by run source: `test` or `production`."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "minimum": 1,
              "description": "Maximum number of runs to return in one page.",
              "default": 50,
              "title": "Limit"
            },
            "description": "Maximum number of runs to return in one page."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 4096
                },
                {
                  "type": "null"
                }
              ],
              "description": "Pagination token from the previous response's `nextCursor`.",
              "title": "Cursor"
            },
            "description": "Pagination token from the previous response's `nextCursor`."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicRunPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/runs/results": {
      "get": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Download a pipeline run's results",
        "description": "Download full raw results for pipeline by its job name",
        "operationId": "getRunResults",
        "parameters": [
          {
            "name": "jobName",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "The run's job name.",
              "title": "Jobname"
            },
            "description": "The run's job name."
          },
          {
            "name": "user",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 320
                },
                {
                  "type": "null"
                }
              ],
              "description": "The run owner's email — needed only to disambiguate a job name shared across accounts.",
              "title": "User"
            },
            "description": "The run owner's email — needed only to disambiguate a job name shared across accounts."
          },
          {
            "name": "node",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1
                },
                {
                  "type": "null"
                }
              ],
              "description": "Scope the ZIP to one step — a `steps[].id` from `GET /runs/{id}`.",
              "title": "Node"
            },
            "description": "Scope the ZIP to one step — a `steps[].id` from `GET /runs/{id}`."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicRunResults"
                }
              }
            }
          },
          "202": {
            "description": "The archive is still being built — GET again to keep waiting."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/runs/{run_id}": {
      "get": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Get a pipeline run",
        "description": "Query a run for its status overall and per-node, along with output results.",
        "operationId": "getRun",
        "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/PublicRun"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    },
    "/api/pipelines/runs/{run_id}/stop": {
      "post": {
        "tags": [
          "pipelines-public"
        ],
        "summary": "Stop a pipeline run",
        "description": "Stop a pipeline run.\n\nAny jobs which have not yet completed are stopped, including running jobs.\nOutputs of any completed jobs are saved and may be viewed.",
        "operationId": "stopRun",
        "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/PublicRun"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationProblem"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "x-doc-group": "Pipelines"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your Tamarind API key. Create one at https://app.tamarind.bio/api-docs/api-key."
      }
    },
    "schemas": {
      "DiagnosticCode": {
        "type": "string",
        "enum": [
          "parse-error",
          "shape-violation",
          "structural-limit",
          "cycle",
          "dangling-ref",
          "unsupported-schema-version",
          "tool-unknown",
          "setting-invalid",
          "param-out-of-range",
          "required-field-unset",
          "input-unbound",
          "input-missing-reference",
          "chain-incompatible",
          "chain-unsatisfied",
          "molecule-class-incompatible",
          "binding-invalid",
          "budget-exceeded",
          "tool-not-licensed",
          "runtime-unresolved",
          "unknown"
        ],
        "title": "DiagnosticCode",
        "description": "The PUBLIC validation-diagnostic vocabulary — the stable value set a caller may switch on.\n\nThis is a CURATED contract, not a passthrough of the internal code set: the mapper\n(`_map/pipelines._diagnostic`) translates each internal code to one of these, and an internal\ncode with no public mapping becomes `unknown` (never a raw internal string). So a new INTERNAL\ndiagnostic code cannot silently enter the public contract — adding a public code is a deliberate,\nv1-frozen change. Keep in sync with the mapper's translation table."
      },
      "Flow": {
        "type": "string",
        "enum": [
          "molecule",
          "file"
        ],
        "title": "Flow"
      },
      "MoleculeChainInfo": {
        "properties": {
          "type": {
            "type": "string",
            "title": "Type"
          },
          "tags": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Tags"
          }
        },
        "type": "object",
        "required": [
          "type",
          "tags"
        ],
        "title": "MoleculeChainInfo",
        "description": "Type + role tags of one chain in a molecule's `entity`, keyed by the same\nchain id — so a reader tells a protein sequence from a SMILES, and sees\nheavy/light/lead roles, without loading the group's schema."
      },
      "MoleculeClass": {
        "type": "string",
        "enum": [
          "protein",
          "small_molecule",
          "nucleic_acid"
        ],
        "title": "MoleculeClass"
      },
      "MoleculeFileEntry": {
        "properties": {
          "fileName": {
            "type": "string",
            "title": "Filename"
          },
          "fileType": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Filetype"
          },
          "downloadUrl": {
            "type": "string",
            "title": "Downloadurl"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Createdat"
          }
        },
        "type": "object",
        "required": [
          "fileName",
          "fileType",
          "downloadUrl",
          "createdAt"
        ],
        "title": "MoleculeFileEntry",
        "description": "One structure file on a molecule. Appears in the `files` map, whose KEY is\nthe producer (a job/tool name, or `user` for uploads)."
      },
      "MoleculeType": {
        "type": "string",
        "enum": [
          "protein",
          "antibody",
          "peptide",
          "enzyme",
          "small_molecule",
          "nucleic_acid",
          "small_molecule_binding_protein"
        ],
        "title": "MoleculeType",
        "description": "The spec's `MoleculeType` — the kind of a molecule, and of the molecules a\ngroup holds.\n\nValue-identical to the internal `Modality` (the user-picked upload modality),\nwhich is the superset enum `complexes.type` is written from. Declared\nseparately because it is a PUBLISHED contract: `Modality` is free to grow a\nvalue for an internal picker without that value silently becoming part of the\npublic API. `public_types_match_modality` pins them equal today."
      },
      "PipelineIR": {
        "properties": {},
        "additionalProperties": true,
        "type": "object",
        "title": "PipelineIR",
        "description": "A pipeline IR document. Full schema (typed, versioned): https://tamarind.bio/schemas/pipeline-v1.json"
      },
      "PublicBinding": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/PublicMoleculeBinding"
          },
          {
            "$ref": "#/components/schemas/PublicFileBinding"
          }
        ],
        "title": "PublicBinding"
      },
      "PublicChainMappingEntry": {
        "properties": {
          "type": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PublicChainType"
              },
              {
                "type": "null"
              }
            ]
          },
          "tags": {
            "anyOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/PublicChainTag"
                },
                "type": "array",
                "maxItems": 3
              },
              {
                "type": "null"
              }
            ],
            "title": "Tags",
            "description": "Roles for this chain. A chain is `heavy` OR `light`, never both (storage keeps one subtype); `lead` is compatible with either. Omit to inherit the schema's tag."
          },
          "csvColumn": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 500,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Csvcolumn"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PublicChainMappingEntry",
        "description": "The spec's `ChainMappingEntry` — how ONE chain is defined for ingestion,\nkeyed by chain id.\n\nThis is what REPLACED the old fixed CSV role vocabulary\n(`heavy_chain`/`light_chain`/`sequence`/...), which could only describe\nantibody-shaped data and could not name a chain id at all (design notes §1)."
      },
      "PublicChainTag": {
        "type": "string",
        "enum": [
          "heavy",
          "light",
          "lead"
        ],
        "title": "PublicChainTag",
        "description": "The spec's `ChainTag` — the functional role of one chain."
      },
      "PublicChainType": {
        "type": "string",
        "enum": [
          "protein",
          "small_molecule"
        ],
        "title": "PublicChainType",
        "description": "The spec's `ChainType` — the molecular kind of one chain."
      },
      "PublicCommitQueued": {
        "properties": {
          "importId": {
            "type": "string",
            "title": "Importid"
          },
          "status": {
            "type": "string",
            "const": "queued",
            "title": "Status",
            "default": "queued"
          },
          "groupName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Groupname"
          }
        },
        "type": "object",
        "required": [
          "importId",
          "groupName"
        ],
        "title": "PublicCommitQueued",
        "description": "Returned when `wait=false` (the default)."
      },
      "PublicCommitRequest": {
        "properties": {
          "columnMapping": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Columnmapping"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PublicCommitRequest",
        "description": "The spec's `CommitRequest` — optional overrides applied at commit time."
      },
      "PublicCreateGroupRequest": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Name"
          },
          "type": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MoleculeType"
              },
              {
                "type": "null"
              }
            ]
          },
          "schemaId": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Schemaid"
          },
          "orgId": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Orgid"
          },
          "tags": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "maxItems": 64,
            "title": "Tags"
          },
          "metadata": {
            "additionalProperties": true,
            "type": "object",
            "title": "Metadata"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name"
        ],
        "title": "PublicCreateGroupRequest",
        "description": "The spec's `CreateGroupRequest`.\n\nInline group creation was dropped from upload/import, so this is now the ONLY\nway a group comes into existence on the public surface."
      },
      "PublicCreateSchemaRequest": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Name"
          },
          "fields": {
            "items": {
              "$ref": "#/components/schemas/PublicSchemaField"
            },
            "type": "array",
            "maxItems": 200,
            "minItems": 1,
            "title": "Fields"
          },
          "orgId": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Orgid"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name",
          "fields"
        ],
        "title": "PublicCreateSchemaRequest"
      },
      "PublicCreateTemplateRequest": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Name"
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "pipeline": {
            "$ref": "#/components/schemas/PipelineIR",
            "description": "The pipeline IR. Every molecule `user_input` node must name a reference group in `metadata.defaultGroup` (the molecules the template is authored against — a run binds its own group at submit); a molecule input without one is rejected 422 `input-missing-reference`. File inputs are exempt."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name",
          "pipeline"
        ],
        "title": "PublicCreateTemplateRequest",
        "example": {
          "description": "AF2 + ProteinMPNN",
          "name": "binder-design",
          "pipeline": {
            "nodes": {
              "af2": {
                "inputs": {
                  "sequence": [
                    {
                      "node": "target"
                    }
                  ]
                },
                "kind": "tool",
                "tool": "tamarind://alphafold"
              },
              "target": {
                "flow": "molecule",
                "kind": "user_input",
                "metadata": {
                  "defaultGroup": "3f8a1c2e-5b7d-4e9f-a1b2-c3d4e5f6a7b8"
                },
                "molecule_type": "protein"
              }
            },
            "schema_version": "1.0"
          }
        }
      },
      "PublicDeleteMoleculeResponse": {
        "properties": {
          "moleculeId": {
            "type": "string",
            "title": "Moleculeid"
          },
          "deleted": {
            "type": "boolean",
            "title": "Deleted"
          }
        },
        "type": "object",
        "required": [
          "moleculeId",
          "deleted"
        ],
        "title": "PublicDeleteMoleculeResponse"
      },
      "PublicDiagnostic": {
        "properties": {
          "code": {
            "$ref": "#/components/schemas/DiagnosticCode"
          },
          "severity": {
            "$ref": "#/components/schemas/Severity"
          },
          "node": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Node"
          },
          "field": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Field"
          },
          "message": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Message"
          }
        },
        "type": "object",
        "required": [
          "code",
          "severity",
          "node"
        ],
        "title": "PublicDiagnostic"
      },
      "PublicDuplicateTemplateRequest": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "version": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Version",
            "description": "A version handle e.g. 'v3'; absent -> default."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PublicDuplicateTemplateRequest",
        "description": "Body for `POST /templates/{templateId}/duplicate`.\n\nBoth fields are optional; `{}` is a valid body.\n\nVERSIONS ONLY — never the working draft. A draft is mutable and unversioned, so there is no\nstable thing for an API client to reference (\"the draft as of now\" isn't expressible) and it\nmay be a teammate's mid-edit graph. So `version` omitted resolves to the published version if\none is pinned, else the latest SAVED version. This is the one place the API deliberately\ndiffers from the in-app menu, which forks the draft because the user can see it.",
        "example": {
          "name": "binder-design v2 experiment",
          "version": "v2"
        }
      },
      "PublicFastaMode": {
        "type": "string",
        "enum": [
          "one-entity-per-file",
          "one-entity-per-header"
        ],
        "title": "PublicFastaMode",
        "description": "The spec's `fastaMode` — how a FASTA file is split into molecules.\n\nDeliberately NOT the internal `FastaMode`'s spelling: the public contract says\n`one-entity-per-file` / `one-entity-per-header` where the internal enum says\n`one-complex-per-file` / `one-complex-per-line`. The public surface drops the\n\"complex\" vocabulary entirely, and `header` describes the FASTA `>` record more\nhonestly than `line`. `to_internal_fasta_mode` is the only bridge."
      },
      "PublicFieldType": {
        "type": "string",
        "enum": [
          "string",
          "integer",
          "float",
          "boolean",
          "category",
          "chain"
        ],
        "title": "PublicFieldType",
        "description": "The spec's `FieldType`.\n\nNOTE `chain` is a MEMBER of this enum, not a separate axis: a schema's `fields`\nlist interleaves scalar fields and chain fields, and `type == \"chain\"` is what\ndistinguishes them."
      },
      "PublicFileBinding": {
        "properties": {
          "file": {
            "type": "string",
            "title": "File",
            "description": "A file path (relative to your user folder)."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "file"
        ],
        "title": "PublicFileBinding"
      },
      "PublicFileFormat": {
        "type": "string",
        "enum": [
          "auto",
          "csv",
          "fasta",
          "sdf",
          "pdb",
          "zip",
          "sdf_zip"
        ],
        "title": "PublicFileFormat",
        "description": "The spec's `FileFormat` — a STRICT SUBSET of the internal\n`MoleculeFileFormat`, which also carries `cif`/`mmcif`. The public API does not\ndocument those, so they aren't accepted here; `auto` still detects anything the\nworker can read."
      },
      "PublicFileImportRequest": {
        "properties": {
          "groupId": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Groupid"
          },
          "fileName": {
            "type": "string",
            "maxLength": 512,
            "minLength": 1,
            "title": "Filename"
          },
          "fileFormat": {
            "$ref": "#/components/schemas/PublicFileFormat",
            "default": "auto"
          },
          "sizeBytes": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 2147483648,
                "minimum": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Sizebytes"
          },
          "columnMapping": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Columnmapping"
          },
          "chainMapping": {
            "anyOf": [
              {
                "additionalProperties": {
                  "$ref": "#/components/schemas/PublicChainMappingEntry"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Chainmapping"
          },
          "fastaMode": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PublicFastaMode"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "groupId",
          "fileName"
        ],
        "title": "PublicFileImportRequest",
        "description": "The spec's `FileImportRequest`."
      },
      "PublicFileImportStart": {
        "properties": {
          "importId": {
            "type": "string",
            "title": "Importid"
          },
          "uploadUrl": {
            "type": "string",
            "title": "Uploadurl"
          },
          "uploadMethod": {
            "type": "string",
            "const": "PUT",
            "title": "Uploadmethod",
            "default": "PUT"
          },
          "uploadHeaders": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "title": "Uploadheaders"
          },
          "groupName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Groupname"
          },
          "expiresInSeconds": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expiresinseconds"
          }
        },
        "type": "object",
        "required": [
          "importId",
          "uploadUrl",
          "uploadHeaders",
          "groupName",
          "expiresInSeconds"
        ],
        "title": "PublicFileImportStart"
      },
      "PublicGroup": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "displayName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Displayname"
          },
          "matchedOn": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Matchedon"
          },
          "type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Type"
          },
          "status": {
            "type": "string",
            "title": "Status"
          },
          "moleculeCount": {
            "type": "integer",
            "title": "Moleculecount"
          },
          "schemaId": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Schemaid"
          },
          "source": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PublicGroupSource"
              },
              {
                "type": "null"
              }
            ]
          },
          "tags": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Tags"
          },
          "metadata": {
            "additionalProperties": true,
            "type": "object",
            "title": "Metadata"
          },
          "createdAt": {
            "type": "string",
            "title": "Createdat"
          },
          "updatedAt": {
            "type": "string",
            "title": "Updatedat"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "displayName",
          "matchedOn",
          "type",
          "status",
          "moleculeCount",
          "schemaId",
          "source",
          "tags",
          "metadata",
          "createdAt",
          "updatedAt"
        ],
        "title": "PublicGroup",
        "description": "The spec's `Group` — a named collection of molecules.\n\nMolecule-only vocabulary: `moleculeCount`, never the internal `complexCount`."
      },
      "PublicGroupPage": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PublicGroup"
            },
            "type": "array",
            "title": "Items"
          },
          "nextCursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nextcursor"
          }
        },
        "type": "object",
        "required": [
          "items",
          "nextCursor"
        ],
        "title": "PublicGroupPage"
      },
      "PublicGroupSource": {
        "properties": {
          "jobId": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Jobid"
          },
          "toolName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Toolname"
          }
        },
        "type": "object",
        "required": [
          "jobId",
          "toolName"
        ],
        "title": "PublicGroupSource",
        "description": "`Group.source` — set when the group is a job's output."
      },
      "PublicImportStatus": {
        "properties": {
          "importId": {
            "type": "string",
            "title": "Importid"
          },
          "status": {
            "type": "string",
            "title": "Status"
          },
          "groupName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Groupname"
          },
          "fileName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Filename"
          },
          "fileFormat": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Fileformat"
          }
        },
        "type": "object",
        "required": [
          "importId",
          "status",
          "groupName",
          "fileName",
          "fileFormat"
        ],
        "title": "PublicImportStatus"
      },
      "PublicInputSlot": {
        "properties": {
          "node": {
            "type": "string",
            "title": "Node",
            "description": "The stable input-node id you bind to."
          },
          "flow": {
            "$ref": "#/components/schemas/Flow"
          },
          "moleculeType": {
            "$ref": "#/components/schemas/MoleculeClass"
          },
          "requiresStructure": {
            "type": "boolean",
            "title": "Requiresstructure"
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label"
          },
          "chains": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Chains"
          },
          "chainLabels": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "title": "Chainlabels"
          },
          "residueFields": {
            "items": {
              "$ref": "#/components/schemas/PublicResidueField"
            },
            "type": "array",
            "title": "Residuefields"
          }
        },
        "type": "object",
        "required": [
          "node",
          "flow",
          "moleculeType",
          "requiresStructure",
          "label",
          "chains",
          "chainLabels",
          "residueFields"
        ],
        "title": "PublicInputSlot"
      },
      "PublicMolecule": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Type"
          },
          "entity": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "title": "Entity"
          },
          "chainMapping": {
            "additionalProperties": {
              "$ref": "#/components/schemas/MoleculeChainInfo"
            },
            "type": "object",
            "title": "Chainmapping"
          },
          "files": {
            "additionalProperties": {
              "items": {
                "$ref": "#/components/schemas/MoleculeFileEntry"
              },
              "type": "array"
            },
            "type": "object",
            "title": "Files"
          },
          "metadata": {
            "additionalProperties": true,
            "type": "object",
            "title": "Metadata"
          },
          "truncated": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Truncated"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Createdat"
          },
          "addedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Addedat"
          },
          "origin": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PublicMoleculeOrigin"
              },
              {
                "type": "null"
              }
            ]
          },
          "source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source"
          },
          "groups": {
            "items": {
              "$ref": "#/components/schemas/PublicGroup"
            },
            "type": "array",
            "title": "Groups"
          },
          "tags": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Tags"
          },
          "notes": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Notes"
          },
          "hasStructure": {
            "type": "boolean",
            "title": "Hasstructure"
          },
          "matchedOn": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Matchedon"
          },
          "sortGroup": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PublicMoleculeSortGroup"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "type",
          "entity",
          "chainMapping",
          "files",
          "metadata",
          "truncated",
          "createdAt",
          "addedAt",
          "origin",
          "source",
          "groups",
          "tags",
          "notes",
          "hasStructure",
          "matchedOn"
        ],
        "title": "PublicMolecule",
        "description": "One molecule, everything inline — no follow-up call to read scores."
      },
      "PublicMoleculeBinding": {
        "properties": {
          "group": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Group",
            "description": "An existing molecules group id."
          },
          "sequences": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sequences",
            "description": "Raw protein sequences to make a group from."
          },
          "smiles": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Smiles",
            "description": "Raw small-molecule SMILES to make a group from."
          },
          "pdbs": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pdbs",
            "description": "Uploaded .pdb file paths (relative to your user folder) to make a group from."
          },
          "sdfs": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sdfs",
            "description": "Uploaded .sdf file paths (relative to your user folder) to make a group from."
          },
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Name for the group created from raw values (auto if omitted)."
          },
          "chainMapping": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Chainmapping",
            "description": "Optional; defaults to identity. referenceChain -> yourChain — only needed for an existing template whose reference chain IDs differ from your molecule's (for an inline pipeline your molecule IS the reference, so omit it)."
          },
          "residuesByChain": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Residuesbychain",
            "description": "referenceChain -> residue selection."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PublicMoleculeBinding",
        "description": "A molecule binding. Provide the molecules ONE of these ways (exactly one):\n\n- `group` — an existing molecules group id, OR\n- `sequences` — raw protein sequences, OR\n- `smiles` — raw small-molecule SMILES, OR\n- `pdbs` / `sdfs` — paths (relative to your user folder) of already-uploaded structure files.\n\nFor the raw-value forms the server creates a molecules group for you (optionally named via `name`),\nthen binds it — so you don't have to pre-create one. The inferred molecule type (protein for\nsequences/pdbs, small molecule for smiles/sdfs) must match the target input node."
      },
      "PublicMoleculeInput": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 500,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "type": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MoleculeType"
              },
              {
                "type": "null"
              }
            ]
          },
          "entity": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "maxProperties": 64,
            "minProperties": 1,
            "title": "Entity"
          },
          "metadata": {
            "additionalProperties": true,
            "type": "object",
            "title": "Metadata"
          },
          "tags": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "maxItems": 64,
            "title": "Tags"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "entity"
        ],
        "title": "PublicMoleculeInput",
        "description": "The spec's `MoleculeInput` — one molecule to create."
      },
      "PublicMoleculeOrigin": {
        "properties": {
          "type": {
            "type": "string",
            "title": "Type"
          },
          "jobId": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Jobid"
          },
          "jobType": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Jobtype"
          }
        },
        "type": "object",
        "required": [
          "type",
          "jobId",
          "jobType"
        ],
        "title": "PublicMoleculeOrigin",
        "description": "`Molecule.origin` — how this molecule came to exist.\n\n`type` is the `complexes.origin_type` provenance class and is ALWAYS present (an\nuploaded molecule is `user_import`). `jobId`/`jobType` name the producing job/batch\nfor a tool-produced molecule; BOTH are null for an upload — an upload has no job, and\nwe do not fabricate one. Read-only, projected from columns that already exist (no\nmigration)."
      },
      "PublicMoleculePage": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PublicMolecule"
            },
            "type": "array",
            "title": "Items"
          },
          "nextCursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nextcursor"
          },
          "mode": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Mode"
          },
          "scanProgress": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Scanprogress"
          }
        },
        "type": "object",
        "required": [
          "items",
          "nextCursor",
          "mode",
          "scanProgress"
        ],
        "title": "PublicMoleculePage",
        "description": "The spec's `MoleculePage` — `{items, nextCursor}` plus the three\nSEARCH-MODE fields below, and nothing else.\n\nDeliberately NOT `Page[PublicMolecule]`: the generic carries a field\n(`sortedServerSide`, the internal sheet's score-cap fallback flag) that the\npublished contract doesn't declare and no public caller can act on. The\nenvelope grew for `mode=sequence`, which is a CHUNKED scan and therefore has\nto say two things a plain page cannot: which search actually ran, and how far\nthrough the scan this page got."
      },
      "PublicMoleculeSortGroup": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "title": "PublicMoleculeSortGroup",
        "description": "`Molecule.sortGroup` — the group this row was ORDERED BY, under `sortBy=groupName`.\n\nA molecule can be in many of your groups, so `sortBy=groupName` has to rank it by ONE\nof them: the alphabetically first EFFECTIVE (displayed) name across all your in-scope\nmemberships. That group is what makes a group's rows arrive contiguously, and it is\nthe group a grouped presentation should file the row under.\n\nIt is served because the inline `groups` list cannot be trusted to contain it: that\nlist is CAPPED and selected by membership recency, so a molecule in more groups than\nthe cap can be ranked by a group the response never carries. Re-deriving the section\nby name-sorting `groups` then files the row under the wrong heading — silently, and\nwith no way for a client to tell.\n\n`name` is the EFFECTIVE name (the rename label when one exists, else the canonical\none) — the exact string the ordering compared, so a section header built from it\ncannot disagree with the position the row was served in. That makes it the\n`displayName`-preferring sibling of `Group.name`, which is always the raw canonical\ncolumn; for a group that was never renamed the two are identical."
      },
      "PublicPublishRequest": {
        "properties": {
          "version": {
            "type": "string",
            "minLength": 1,
            "title": "Version",
            "description": "The version handle to publish e.g. 'v1' (from a prior response's version)."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "version"
        ],
        "title": "PublicPublishRequest",
        "example": {
          "version": "v1"
        }
      },
      "PublicPublishResponse": {
        "properties": {
          "templateId": {
            "type": "string",
            "title": "Templateid"
          },
          "isPublished": {
            "type": "boolean",
            "title": "Ispublished"
          },
          "publishedVersion": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Publishedversion",
            "description": "The published version handle e.g. 'v1'."
          }
        },
        "type": "object",
        "required": [
          "templateId",
          "isPublished",
          "publishedVersion"
        ],
        "title": "PublicPublishResponse"
      },
      "PublicRemoveMoleculesFromGroupRequest": {
        "properties": {
          "groupId": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Groupid"
          },
          "moleculeIds": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "maxItems": 1000,
            "minItems": 1,
            "title": "Moleculeids"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "groupId",
          "moleculeIds"
        ],
        "title": "PublicRemoveMoleculesFromGroupRequest",
        "description": "Body for `POST /molecules/remove`: the group to detach FROM plus the molecules\nto detach — the group id travels in the body alongside the ids. Same `moleculeIds` cap (maxItems, a real\nstatement bound — every id is a bound `uuid[]` parameter, never inlined)."
      },
      "PublicRemoveMoleculesResponse": {
        "properties": {
          "removedIds": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Removedids"
          },
          "removedCount": {
            "type": "integer",
            "title": "Removedcount"
          }
        },
        "type": "object",
        "required": [
          "removedIds",
          "removedCount"
        ],
        "title": "PublicRemoveMoleculesResponse"
      },
      "PublicResidueField": {
        "properties": {
          "node": {
            "type": "string",
            "title": "Node"
          },
          "field": {
            "type": "string",
            "title": "Field"
          },
          "multichain": {
            "type": "boolean",
            "title": "Multichain"
          },
          "targetsChains": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Targetschains"
          }
        },
        "type": "object",
        "required": [
          "node",
          "field",
          "multichain",
          "targetsChains"
        ],
        "title": "PublicResidueField"
      },
      "PublicRun": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "templateId": {
            "type": "string",
            "title": "Templateid"
          },
          "templateVersion": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Templateversion",
            "description": "The executed version handle e.g. 'v1'."
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "source": {
            "$ref": "#/components/schemas/Source"
          },
          "status": {
            "$ref": "#/components/schemas/RunStatus"
          },
          "startedAt": {
            "type": "string",
            "title": "Startedat"
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completedat"
          },
          "inputs": {
            "additionalProperties": true,
            "type": "object",
            "title": "Inputs",
            "description": "The recorded inputs (input-node id -> {group} or {file})."
          },
          "steps": {
            "items": {
              "$ref": "#/components/schemas/PublicStep"
            },
            "type": "array",
            "title": "Steps"
          }
        },
        "type": "object",
        "required": [
          "id",
          "templateId",
          "templateVersion",
          "name",
          "source",
          "status",
          "startedAt",
          "completedAt",
          "inputs",
          "steps"
        ],
        "title": "PublicRun"
      },
      "PublicRunPage": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PublicRunSummary"
            },
            "type": "array",
            "title": "Items"
          },
          "nextCursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nextcursor"
          }
        },
        "type": "object",
        "required": [
          "items",
          "nextCursor"
        ],
        "title": "PublicRunPage"
      },
      "PublicRunResults": {
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "ready",
              "failed"
            ],
            "title": "Status",
            "description": "`processing` (building / not finished), `ready` (download `url` set), or `failed`."
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Url",
            "description": "A short-lived signed download URL — present only when `status` is `ready`."
          },
          "node": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Node",
            "description": "The scoped step id, echoed back when `?node=` was supplied."
          }
        },
        "type": "object",
        "required": [
          "status",
          "url",
          "node"
        ],
        "title": "PublicRunResults",
        "description": "`GET /runs/results` — the run's (or one node's) output ZIP, produced asynchronously.\n\nPoll this: `processing` while the archive is being built (or the run/step isn't finished yet),\n`ready` with a short-lived signed `url` once it's available, `failed` if the build failed. Polling\nis idempotent — it never starts a duplicate build.\n\n`url`/`node` carry NO default (this surface's required-at-construction convention, see the module\ndocstring): both are always present in the response — `null` when not applicable (`url` unless\n`ready`; `node` unless a step was requested) — so a client can rely on the stable key set."
      },
      "PublicRunSummary": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "templateId": {
            "type": "string",
            "title": "Templateid"
          },
          "templateVersion": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Templateversion",
            "description": "The executed version handle e.g. 'v1'."
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "source": {
            "$ref": "#/components/schemas/Source"
          },
          "status": {
            "$ref": "#/components/schemas/RunStatus"
          },
          "startedAt": {
            "type": "string",
            "title": "Startedat"
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completedat"
          }
        },
        "type": "object",
        "required": [
          "id",
          "templateId",
          "templateVersion",
          "name",
          "source",
          "status",
          "startedAt",
          "completedAt"
        ],
        "title": "PublicRunSummary"
      },
      "PublicSchema": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "fields": {
            "items": {
              "$ref": "#/components/schemas/PublicSchemaField"
            },
            "type": "array",
            "title": "Fields"
          },
          "createdAt": {
            "type": "string",
            "title": "Createdat"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "fields",
          "createdAt"
        ],
        "title": "PublicSchema",
        "description": "The spec's `Schema`."
      },
      "PublicSchemaField": {
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Name"
          },
          "type": {
            "$ref": "#/components/schemas/PublicFieldType",
            "default": "string"
          },
          "required": {
            "type": "boolean",
            "title": "Required",
            "default": false
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "units": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "title": "Units"
          },
          "options": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array",
                "maxItems": 500
              },
              {
                "type": "null"
              }
            ],
            "title": "Options",
            "description": "Allowed values for a `category` field. REQUIRED (non-empty) when `type` is `category`, and must be omitted for every other type."
          },
          "chainType": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PublicChainType"
              },
              {
                "type": "null"
              }
            ]
          },
          "chainTag": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PublicChainTag"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name"
        ],
        "title": "PublicSchemaField",
        "description": "The spec's `SchemaField` — one field in a schema.\n\nA RequestModel (extra='forbid') even though it also appears on responses: the\nfield names are already the wire spelling, so no alias generator is needed, and\nforbidding extras means a typo'd `chaintype` 422s at create instead of being\nsilently stored in the JSONB and never enforced.\n\n`type` defaults to `string` per the spec. `chain` is one of its values — a\nchain field's `name` IS the chain id (`H`, `L`, `A`), matching the keys of a\nmolecule's `entity` map."
      },
      "PublicSchemaPage": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PublicSchema"
            },
            "type": "array",
            "title": "Items"
          },
          "nextCursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nextcursor"
          }
        },
        "type": "object",
        "required": [
          "items",
          "nextCursor"
        ],
        "title": "PublicSchemaPage"
      },
      "PublicStep": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "node": {
            "type": "string",
            "title": "Node",
            "description": "The stable IR node id."
          },
          "label": {
            "type": "string",
            "title": "Label"
          },
          "type": {
            "type": "string",
            "title": "Type"
          },
          "status": {
            "$ref": "#/components/schemas/StepStatus"
          },
          "startedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Startedat"
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completedat"
          },
          "outputCount": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Outputcount"
          },
          "jobsTotal": {
            "type": "integer",
            "title": "Jobstotal"
          },
          "jobsComplete": {
            "type": "integer",
            "title": "Jobscomplete"
          },
          "outputGroup": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Outputgroup",
            "description": "The molecules group id this step produced."
          }
        },
        "type": "object",
        "required": [
          "id",
          "node",
          "label",
          "type",
          "status",
          "startedAt",
          "completedAt",
          "outputCount",
          "jobsTotal",
          "jobsComplete",
          "outputGroup"
        ],
        "title": "PublicStep"
      },
      "PublicSubmitPipelineRequest": {
        "properties": {
          "pipeline": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PipelineIR"
              },
              {
                "type": "null"
              }
            ],
            "description": "INLINE mode: a full pipeline IR. Mutually exclusive with `templateId`."
          },
          "templateId": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Templateid",
            "description": "REFERENCE mode: an existing template id to run."
          },
          "name": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Name",
            "description": "Name for this pipeline. When `runName` is omitted, the run's JobName is derived from this (spaces removed, a random suffix appended for uniqueness)."
          },
          "runName": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Runname",
            "description": "Optional explicit JobName for THIS run. Spaces are replaced with underscores; used verbatim otherwise (no random suffix). Omit to auto-generate a unique JobName from `name`."
          },
          "version": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Version",
            "description": "REFERENCE mode: a version handle e.g. 'v1'; absent -> default."
          },
          "bindings": {
            "additionalProperties": {
              "$ref": "#/components/schemas/PublicBinding"
            },
            "type": "object",
            "maxProperties": 2048,
            "title": "Bindings",
            "description": "One binding per input slot, keyed by the slot's input-node id. Bindings may be defined as an array of sequences, smiles, or sdf/pdb files (uploaded using the /upload endpoint), or an existing molecule group id (created using /molecules/groups below)."
          },
          "settings": {
            "anyOf": [
              {
                "additionalProperties": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Settings",
            "description": "REFERENCE mode: per-node setting overrides `{nodeId: {settingKey: value}}`, limited to the template's editable settings."
          },
          "source": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Source"
              },
              {
                "type": "null"
              }
            ],
            "description": "Run source; defaults to production.",
            "default": "production"
          },
          "project": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Project",
            "description": "Organization project id to stamp on jobs this run creates."
          },
          "idempotencyKey": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 255,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Idempotencykey",
            "description": "Client key to safely retry a submit (≤255)."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name",
          "bindings"
        ],
        "title": "PublicSubmitPipelineRequest",
        "description": "Body for `POST /pipelines/submit` AND `POST /pipelines/validate` (identical shape). Two modes,\ndiscriminated by which of `pipeline` / `templateId` you send — exactly one is required:\n\n- INLINE: send `pipeline` (a full IR) — submit creates a template you own (unpublished) and runs\n  it; every setting is yours to set in the IR. `name` names both the created pipeline and the run.\n- REFERENCE: send `templateId` (+ optional `version`) — run an existing template; `settings`\n  overrides are limited to each tool node's `metadata.editableSettings`. `name` names the run.\n\n`name` is required and doubles as the run's display name (deduplicated per pipeline). `bindings` is\nrequired in both modes. `validate` runs the SAME body without executing/persisting.",
        "example": {
          "bindings": {
            "target": {
              "group": "3f8a1c2e-5b7d-4e9f-a1b2-c3d4e5f6a7b8"
            }
          },
          "name": "binder-design v2 experiment",
          "project": "proj_…",
          "settings": {
            "design": {
              "numSequences": 32
            }
          },
          "templateId": "3f2a1c9e-8b7d-4e6f-a1b2-c3d4e5f60718",
          "version": "v1"
        }
      },
      "PublicTemplate": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "isPublished": {
            "type": "boolean",
            "title": "Ispublished"
          },
          "publishedVersion": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Publishedversion",
            "description": "The published version handle e.g. 'v1' (null if none)."
          },
          "version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Version",
            "description": "The version handle e.g. 'v1' (null if never saved)."
          },
          "pipeline": {
            "$ref": "#/components/schemas/PipelineIR"
          },
          "inputs": {
            "items": {
              "$ref": "#/components/schemas/PublicInputSlot"
            },
            "type": "array",
            "title": "Inputs"
          },
          "versions": {
            "items": {
              "$ref": "#/components/schemas/PublicVersionSummary"
            },
            "type": "array",
            "title": "Versions",
            "description": "All saved versions, newest first."
          },
          "createdAt": {
            "type": "string",
            "title": "Createdat"
          },
          "updatedAt": {
            "type": "string",
            "title": "Updatedat"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "description",
          "isPublished",
          "publishedVersion",
          "version",
          "pipeline",
          "inputs",
          "versions",
          "createdAt",
          "updatedAt"
        ],
        "title": "PublicTemplate"
      },
      "PublicTemplatePage": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PublicTemplateSummary"
            },
            "type": "array",
            "title": "Items"
          },
          "nextCursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nextcursor"
          }
        },
        "type": "object",
        "required": [
          "items",
          "nextCursor"
        ],
        "title": "PublicTemplatePage"
      },
      "PublicTemplateSummary": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "isPublished": {
            "type": "boolean",
            "title": "Ispublished"
          },
          "versionCount": {
            "type": "integer",
            "title": "Versioncount"
          },
          "runCount": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Runcount"
          },
          "createdBy": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Createdby"
          },
          "createdAt": {
            "type": "string",
            "title": "Createdat"
          },
          "updatedAt": {
            "type": "string",
            "title": "Updatedat"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "description",
          "isPublished",
          "versionCount",
          "runCount",
          "createdBy",
          "createdAt",
          "updatedAt"
        ],
        "title": "PublicTemplateSummary"
      },
      "PublicUpdateMetadataRequest": {
        "properties": {
          "properties": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Properties"
          },
          "source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source"
          },
          "fileId": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Fileid"
          },
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "metadata": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Metadata",
            "description": "DEPRECATED alias for `properties`, kept for callers written against the original v1 shape. Send `properties` instead; if both are sent, `properties` wins."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PublicUpdateMetadataRequest",
        "description": "The spec's `UpdateMoleculeRequest` — a PARTIAL patch across the MOLECULE and\nMEMBERSHIP tenancy grains, applied ATOMICALLY (all-or-nothing, one transaction: if any\nfield's write fails, none of them persist).\n\nEvery field is OPTIONAL and independent: an ABSENT field is left untouched, which\nis DISTINCT from a field sent as `null` (which CLEARS it, where the grain allows).\nThe fields and the grain each writes:\n\n  * `properties` (MOLECULE grain) — merge scalar annotations into the molecule's\n    metadata. Only the keys you send change; a key set to `null` REMOVES that key\n    (an absent key and a null key mean different things, so the raw dict carries\n    intent). Tool score-run entries are written by tools and are not editable here;\n    when the molecule's group is schema-bound the MERGED result must still satisfy\n    it. This is the properties-only PATCH's behaviour, unchanged.\n  * `source` (MOLECULE grain) — the id of the PARENT molecule this one was derived\n    from; `null` clears it. The parent must be visible in YOUR scope, or the write\n    is refused (404) — you cannot point a molecule at a parent you cannot see.\n  * `fileId` (MOLECULE + MEMBERSHIP grain) — a structure file to associate as this\n    membership's primary; `null` clears it. The file must already be attached to\n    this molecule in your tenant.\n  * `name` (MEMBERSHIP grain) — rename this molecule's per-group display name. A\n    name already used by another molecule in the same group is a 409 conflict (the\n    `UNIQUE (group_id, name)` constraint), never a 500.\n\nThe membership-grained writes (`name`, `fileId`) target the molecule's MOST RECENT\nin-scope group membership, matching how the group-less by-id read resolves labels."
      },
      "PublicUpdateSchemaRequest": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "fields": {
            "anyOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/PublicSchemaField"
                },
                "type": "array",
                "maxItems": 200,
                "minItems": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Fields"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PublicUpdateSchemaRequest",
        "description": "The spec's `UpdateSchemaRequest` — partial. `fields` REPLACES the whole\nlist; omitted keys are left unchanged."
      },
      "PublicUploadRequest": {
        "properties": {
          "groupId": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Groupid"
          },
          "chainMapping": {
            "additionalProperties": {
              "$ref": "#/components/schemas/PublicChainMappingEntry"
            },
            "type": "object",
            "title": "Chainmapping"
          },
          "molecules": {
            "items": {
              "$ref": "#/components/schemas/PublicMoleculeInput"
            },
            "type": "array",
            "maxItems": 1000,
            "minItems": 1,
            "title": "Molecules"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "groupId",
          "molecules"
        ],
        "title": "PublicUploadRequest",
        "description": "The spec's `UploadRequest`."
      },
      "PublicUploadResponse": {
        "properties": {
          "groupId": {
            "type": "string",
            "title": "Groupid"
          },
          "groupName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Groupname"
          },
          "moleculeIds": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Moleculeids"
          },
          "moleculeCount": {
            "type": "integer",
            "title": "Moleculecount"
          },
          "status": {
            "type": "string",
            "const": "pending",
            "title": "Status",
            "default": "pending"
          },
          "importId": {
            "type": "string",
            "title": "Importid"
          }
        },
        "type": "object",
        "required": [
          "groupId",
          "groupName",
          "moleculeIds",
          "moleculeCount",
          "importId"
        ],
        "title": "PublicUploadResponse"
      },
      "PublicValidateResponse": {
        "properties": {
          "valid": {
            "type": "boolean",
            "title": "Valid"
          },
          "errors": {
            "items": {
              "$ref": "#/components/schemas/PublicDiagnostic"
            },
            "type": "array",
            "title": "Errors"
          }
        },
        "type": "object",
        "required": [
          "valid",
          "errors"
        ],
        "title": "PublicValidateResponse"
      },
      "PublicValidateTemplateRequest": {
        "properties": {
          "version": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Version",
            "description": "A version handle e.g. 'v1' to validate; absent -> default."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "title": "PublicValidateTemplateRequest",
        "description": "Body for `POST /templates/{id}/validate` — validate the TEMPLATE ITSELF (no run bindings),\nagainst its own `metadata.defaultGroup` reference groups. `{}` is valid (validate the default\nversion)."
      },
      "PublicVersionSummary": {
        "properties": {
          "version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Version",
            "description": "The version handle e.g. 'v1'."
          },
          "createdAt": {
            "type": "string",
            "title": "Createdat"
          },
          "isPublished": {
            "type": "boolean",
            "title": "Ispublished"
          },
          "isValid": {
            "type": "boolean",
            "title": "Isvalid"
          }
        },
        "type": "object",
        "required": [
          "version",
          "createdAt",
          "isPublished",
          "isValid"
        ],
        "title": "PublicVersionSummary"
      },
      "RunStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "finished",
          "partial",
          "stopped",
          "failed"
        ],
        "title": "RunStatus"
      },
      "Severity": {
        "type": "string",
        "enum": [
          "error",
          "warning"
        ],
        "title": "Severity"
      },
      "Source": {
        "type": "string",
        "enum": [
          "production",
          "test"
        ],
        "title": "Source"
      },
      "StepStatus": {
        "type": "string",
        "enum": [
          "waiting",
          "queued",
          "running",
          "finished",
          "failed",
          "skipped",
          "stopped",
          "cancelled"
        ],
        "title": "StepStatus"
      },
      "PublicProblem": {
        "description": "RFC 9457 problem detail. Serialized as `application/problem+json` on every public error.",
        "properties": {
          "type": {
            "description": "A URI identifying the error kind (dereferenceable docs).",
            "title": "Type",
            "type": "string"
          },
          "title": {
            "description": "A short, human-readable summary of the error kind.",
            "title": "Title",
            "type": "string"
          },
          "status": {
            "description": "The HTTP status code, duplicated in the body per RFC 9457.",
            "title": "Status",
            "type": "integer"
          },
          "code": {
            "description": "A stable machine-readable slug; switch on THIS, not prose.",
            "title": "Code",
            "type": "string"
          },
          "detail": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Instance-specific human explanation.",
            "title": "Detail"
          },
          "errors": {
            "anyOf": [
              {
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Structured per-item detail (request-validation fields OR pipeline diagnostics).",
            "title": "Errors"
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "title": "PublicProblem",
        "type": "object"
      },
      "JobSubmission": {
        "type": "object",
        "required": [
          "jobName",
          "type",
          "settings"
        ],
        "properties": {
          "jobName": {
            "type": "string",
            "description": "Name for the job, unique within your account. Characters outside [A-Za-z0-9_.- ] are stripped and whitespace becomes underscores, so a name is sanitised rather than rejected.",
            "minLength": 1,
            "example": "my-protein-analysis"
          },
          "type": {
            "type": "string",
            "description": "Tool to run. The available tools are account-scoped — fetch the live list from `GET /tools` rather than assuming a name.",
            "example": "alphafold"
          },
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Tool-specific settings. The accepted fields differ per tool and per account, so they are not enumerated here: fetch the JSON Schema for the tool you are submitting from `GET /tools/{name}/schema` and validate against that. `POST /validate-job` checks a payload for free.",
            "example": {
              "sequence": "MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQ"
            }
          }
        }
      },
      "BatchSubmission": {
        "type": "object",
        "required": [
          "batchName",
          "type",
          "settings"
        ],
        "properties": {
          "batchName": {
            "type": "string",
            "description": "Name for the batch, unique within your account. A batch name is held by a lock for 15 minutes after submission, so reusing one within that window returns 409.",
            "minLength": 1,
            "example": "my-batch-analysis"
          },
          "type": {
            "type": "string",
            "description": "Tool to run for every job in the batch.",
            "example": "alphafold"
          },
          "settings": {
            "type": "array",
            "description": "One settings object per job — the array form is what distinguishes this endpoint from `/submit-job`. See `JobSubmission.settings` for where the per-tool shape comes from.",
            "minItems": 1,
            "maxItems": 30000,
            "items": {
              "$ref": "#/components/schemas/JobSubmission/properties/settings"
            }
          },
          "jobNames": {
            "type": "array",
            "description": "Optional names, the same length as `settings`. Omit to have jobs auto-named. If any name collides with an existing job, every name is rewritten as `{batchName}-{name}`.",
            "minItems": 1,
            "maxItems": 30000,
            "items": {
              "$ref": "#/components/schemas/JobSubmission/properties/jobName"
            }
          }
        }
      },
      "JobResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Response message",
            "example": "Job submitted successfully"
          }
        }
      },
      "BatchResponse": {
        "type": "object",
        "properties": {
          "batchName": {
            "type": "string",
            "description": "Name of the submitted batch"
          },
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JobResponse"
            }
          },
          "totalJobs": {
            "type": "integer",
            "description": "Total number of jobs in the batch"
          }
        }
      },
      "JobInfo": {
        "type": "object",
        "properties": {
          "JobName": {
            "type": "string",
            "description": "Name of the job"
          },
          "Type": {
            "type": "string",
            "description": "Tool used for the job"
          },
          "JobStatus": {
            "type": "string",
            "enum": [
              "Complete",
              "In Queue",
              "Running",
              "Stopped",
              "Deleted"
            ],
            "description": "Current status of the job"
          },
          "Created": {
            "type": "string",
            "format": "date-time",
            "description": "When the job was created"
          },
          "Started": {
            "type": "string",
            "format": "date-time",
            "description": "When the job started (if applicable)"
          },
          "Completed": {
            "type": "string",
            "format": "date-time",
            "description": "When the job completed (if applicable)"
          },
          "Settings": {
            "type": "object",
            "description": "Job settings and parameters (stored as JSON string)",
            "additionalProperties": true
          },
          "Score": {
            "type": "number",
            "description": "Job score (if applicable)"
          },
          "Batch": {
            "type": "boolean",
            "description": "Whether this is a batch job"
          },
          "User": {
            "type": "string",
            "description": "User who created the job (only present in organization queries)"
          },
          "batchStatus": {
            "type": "string",
            "enum": [
              "Running",
              "Aggregating",
              "Complete",
              "Stopped",
              "AggregationFailed"
            ],
            "description": "Batch-level phase. Present only on a batch's parent job — fetch it with ?jobName=<batchName>. The individual subjobs report Complete as soon as they finish computing, but the batch then spends a few minutes aggregating their results into the final output files. Poll this field instead: `Aggregating` means the output is still being prepared, and `Complete` means the aggregated batch output is ready to download. `AggregationFailed` means aggregation errored (see AggregationError)."
          },
          "AggregationError": {
            "type": "string",
            "description": "Error message, present when batchStatus is AggregationFailed"
          },
          "resultUrl": {
            "type": "string",
            "description": "Present on a single-job lookup (jobName=<batchName>) of a batch parent once batchStatus is Complete: a pre-signed URL (valid ~1 hour) that downloads the aggregated output directly — no extra call or API key needed. Omitted from list responses."
          },
          "aggregateStatus": {
            "type": "string",
            "enum": [
              "InProgress",
              "Complete",
              "Failed"
            ],
            "description": "Status of the on-demand batch-aggregate worker that builds the result zip when the natural-completion path didn't (large batches, retries, etc.). Present only on a single-job lookup (jobName=<batchName>) when a worker exists for this caller and parent. `InProgress` — the zip is being built; wait for `resultUrl` to appear, then download. `Complete` — the worker finished; `resultUrl` should be present. `Failed` — retry /result to spawn a new worker."
          },
          "aggregateJobName": {
            "type": "string",
            "description": "Name of the batch-aggregate worker job (when aggregateStatus is set). Can be polled directly via /jobs?jobName=<aggregateJobName> to watch JobStatus."
          }
        }
      },
      "JobResult": {
        "type": "object",
        "properties": {
          "jobName": {
            "type": "string",
            "description": "Name of the job"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "completed",
              "failed",
              "cancelled"
            ],
            "description": "Current status of the job"
          },
          "results": {
            "type": "object",
            "description": "Job results (structure varies by tool)",
            "additionalProperties": true
          },
          "error": {
            "type": "string",
            "description": "Error message if job failed"
          },
          "outputFiles": {
            "type": "array",
            "description": "List of output files",
            "items": {
              "type": "object",
              "properties": {
                "fileName": {
                  "type": "string",
                  "description": "Name of the output file"
                },
                "fileUrl": {
                  "type": "string",
                  "description": "URL to download the file"
                },
                "fileType": {
                  "type": "string",
                  "description": "Type of the file (PDB, JSON, etc.)"
                }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Error message"
          },
          "detail": {
            "type": "string",
            "description": "Error message (V2 alias — same text as `error`, for FastAPI DomainException compatibility)"
          },
          "code": {
            "type": "string",
            "description": "Error code"
          },
          "details": {
            "type": "object",
            "description": "Additional error details"
          }
        }
      },
      "ToolInfo": {
        "type": "object",
        "description": "One tool in the catalogue. `settings` describes its parameters; fetch `GET /tools/{name}/schema` for the same information as JSON Schema.\n\n`taskType` is the tool's pipeline task category (`structure-prediction`, `inverse-folding`, `score`, …) — the fact pipelines chain on. Two tools connect when the molecule type one produces matches what the next consumes (a `pdb` output may also feed a `sequence` input, since sequences are read from structures).",
        "properties": {
          "name": {
            "type": "string",
            "description": "The value to send as `type` when submitting.",
            "example": "alphafold"
          },
          "displayName": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "github": {
            "type": "string"
          },
          "paper": {
            "type": "string"
          },
          "settings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "required": {
                  "type": "boolean"
                },
                "type": {
                  "type": "string",
                  "description": "Present only for a subset of parameter kinds — read it defensively, and prefer the JSON Schema from `/tools/{name}/schema`."
                },
                "description": {
                  "type": "string"
                },
                "options": {
                  "type": "array",
                  "items": {}
                },
                "default": {},
                "extension": {
                  "type": "array",
                  "description": "For a file parameter, the file formats it accepts — the only published statement of what this tool's parser can read.",
                  "items": {
                    "type": "string"
                  },
                  "example": [
                    "pdb",
                    "cif"
                  ]
                },
                "list": {
                  "type": "boolean",
                  "description": "True when this parameter takes an ARRAY of the stated `type` rather than a single value."
                }
              }
            }
          },
          "outputTypes": {
            "type": "array",
            "description": "What the tool declares it produces — molecular (`pdb`, `sequence`, `sdf`, `smiles`) alongside file and score types (`csv`, `score`, `cif`, …). A few tools use compound values such as `pdb-list`, so match by containment rather than equality.\n\nThis describes the ARTIFACTS a run leaves behind. To chain stages, match on `taskType` and the molecule types: a scoring stage passes its input type through even though its `outputTypes` says `score`. Absent means the tool declares nothing, which is unknown rather than \"produces nothing\"; and a type here is not proof the tool generated it, since a scoring tool can echo its input.",
            "items": {
              "type": "string"
            },
            "example": [
              "pdb"
            ]
          },
          "taskType": {
            "type": "string",
            "description": "The tool's pipeline task category (`structure-prediction`, `inverse-folding`, `score`, …) — the fact pipelines chain on. Absent for a tool that declares none.",
            "example": "structure-prediction"
          },
          "filterMetrics": {
            "type": "array",
            "description": "Metric names accepted in a pipeline stage's filter settings. Deliberately narrower than `outputs.columns` — `/submit-pipeline` rejects a filter on a column that is not filterable. Absent for tools whose filters are not validated.",
            "items": {
              "type": "string"
            }
          },
          "outputs": {
            "type": "object",
            "description": "The tool's result table, when it declares one. (The tool's `taskType` is a TOP-LEVEL field, not nested here.)",
            "properties": {
              "produces": {
                "type": "array",
                "description": "Molecular representations the output carries — as a column of the result table OR as a file written alongside it — INCLUDING any carried over from the input, so a scoring tool can list one here.",
                "items": {
                  "type": "string"
                }
              },
              "mainCSV": {
                "type": "string",
                "description": "Filename of the primary results CSV."
              },
              "byTask": {
                "type": "object",
                "description": "For a tool whose output depends on the task it runs: the per-task contract, keyed by task value, and authoritative for the task you set. Each entry is COMPLETE — a task that does not redeclare `mainCSV` or `taskType` inherits the tool's top-level value (the top-level `taskType` field and this block's `mainCSV`).\n\nThe top-level `taskType` and this block's `mainCSV`/`produces` summarize ACROSS tasks — `produces` is the union over every task's table, and `taskType`/`mainCSV` are the tool's top-level declaration, which is not guaranteed to be the task selector's default. Do not read them as describing the run you are about to submit.",
                "additionalProperties": {
                  "type": "object",
                  "properties": {
                    "taskType": {
                      "type": "string"
                    },
                    "mainCSV": {
                      "type": "string"
                    },
                    "generates": {
                      "type": "array",
                      "description": "What this task creates FRESH — distinct from the sibling `produces`, which is what the result table contains. Empty for a scoring task, whose table may still echo its input.",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "byTaskNote": {
                "type": "string",
                "description": "Present alongside `byTask`: restates in prose how to read the per-task contract against the across-task scalars."
              },
              "columns": {
                "type": "array",
                "description": "Columns of the main CSV. To FILTER on one in a pipeline, check `filterMetrics` — not every column is filterable.",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Column name as it appears in the CSV."
                    },
                    "type": {
                      "type": "string",
                      "description": "Column type: pdb, sequence, number, string, …"
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "units": {
                      "type": "string"
                    },
                    "scoringPropertyName": {
                      "type": "string",
                      "description": "Name this metric is stored under when results are ingested, for tools that expose the column as a scoring property."
                    },
                    "recommendedRange": {
                      "type": "array",
                      "description": "Advisory [min, max] for a good value; either end may be an empty string when unbounded on that side.",
                      "items": {}
                    },
                    "lowIsGood": {
                      "type": "boolean",
                      "description": "Present and true when a LOWER value is better (RMSD, PAE, energy) — the direction to rank in."
                    },
                    "tasks": {
                      "type": "array",
                      "description": "Present only on a task-gated tool: the tasks whose results include this column. An UNTAGGED column is simply not task-gated in the tool's declaration, which does not promise every task fills it.",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ValidationResult": {
        "type": "object",
        "required": [
          "valid"
        ],
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "normalized": {
            "type": "object",
            "additionalProperties": true,
            "description": "Present when valid — the settings to submit, with defaults filled in."
          },
          "error": {
            "type": "string",
            "description": "Present when invalid — the first problem found."
          },
          "missing_fields": {
            "type": "array",
            "description": "Best-effort list of required inputs still missing. May be empty even when `valid` is false, because validation stops at the first error.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "displayName": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "example": {}
              }
            }
          }
        }
      },
      "PipelineStage": {
        "type": "object",
        "required": [
          "task",
          "toolSettings"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Ignored on submit. The server assigns each stage a 1-based index and uses that in error messages, overwriting anything sent here."
          },
          "task": {
            "type": "string",
            "description": "What this stage does, e.g. \"Structure Prediction\"."
          },
          "tools": {
            "type": "array",
            "description": "Optional and derived — submission overwrites it with the keys of `toolSettings` (submit-pipeline.js), so an empty array is accepted. It only widens the set checked against your account's tool access, so naming a tool here without settings for it grants nothing.",
            "items": {
              "type": "string"
            }
          },
          "toolSettings": {
            "type": "object",
            "additionalProperties": true,
            "minProperties": 1,
            "description": "Settings per tool, keyed by tool name — this is what determines which tools the stage runs, so it may not be empty. Each value follows that tool's schema from `GET /tools/{name}/schema`."
          },
          "scoringTools": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scoringToolSettings": {
            "type": "object",
            "additionalProperties": true
          },
          "filterSettings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Metric filters applied to this stage's outputs. Metric names are case-sensitive and tool-specific; an unknown one is rejected with the valid options listed."
          }
        }
      },
      "DeployedModel": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Use this as the `type` when submitting a job."
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "created": {
            "type": "string"
          },
          "gpu": {
            "type": "boolean"
          },
          "environment": {
            "type": "string"
          },
          "entrypoint": {
            "type": "string"
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "testUrl": {
            "type": "string",
            "description": "Present on deploy — a page for trying the model."
          },
          "email": {
            "type": "string",
            "description": "Present only when the model belongs to another member of your organization."
          }
        }
      },
      "FinetunedModel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "description": "Use this as the `modelName` when submitting."
          },
          "type": {
            "type": "string"
          },
          "inferenceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "baseModel": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "created": {
            "type": "string"
          },
          "owner": {
            "type": "string",
            "description": "Present only when the model belongs to another member of your organization."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "API key missing or invalid.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/PublicProblem"
            }
          }
        }
      },
      "NotFound": {
        "description": "The addressed resource does not exist, or is not visible to your tenant.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/PublicProblem"
            }
          }
        }
      },
      "ValidationProblem": {
        "description": "The request was malformed or failed validation; see `errors` for the offending fields.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/PublicProblem"
            }
          }
        }
      },
      "Error": {
        "description": "An error occurred (RFC 9457 problem+json). Switch on the stable `code`, not on prose.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/PublicProblem"
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Jobs",
      "description": "Job submission and management"
    },
    {
      "name": "Tools",
      "description": "The tool catalogue"
    },
    {
      "name": "Files",
      "description": "File upload and management"
    },
    {
      "name": "Results",
      "description": "Job results retrieval"
    },
    {
      "name": "Models",
      "description": "Finetuned models"
    },
    {
      "name": "Usage",
      "description": "Usage statistics"
    },
    {
      "name": "Pipelines",
      "description": "Legacy saved pipelines"
    }
  ]
}