{
  "openapi": "3.0.3",
  "info": {
    "title": "TestDino Public API",
    "version": "1.0.0",
    "description": "Public API for TestDino: read access to test analytics, manual tests, and project data, plus\nmanual-testing write endpoints.\n\n**Read (GET) endpoints** cover analytics, runs, cases, and project data. **Write endpoints\n(POST/PATCH)** create and update manual-testing entities: suites, cases, releases, sessions, and\nmanual runs (including per-case verdicts). Query parameters are **endpoint-specific**; see each\noperation for supported filters, enums, and defaults.\n\nWrites require a PAT whose owner holds a writer role (owner/admin/member) on the project's\norganization; a viewer-role token gets `403`.\n\n## Authentication\n\nAll endpoints require a Bearer token in the `Authorization` header:\n```\nAuthorization: Bearer td_pat_<token>\n```\n\nUse a user PAT (`td_pat_` prefix) scoped to the organization/project.\n\n## Rate Limiting\n\n- **Reads (per-token):** 100 requests/minute\n- **Writes (per-token):** 60 requests/minute\n- **Manual run creation (per-token):** 10 requests/minute\n- **Per-IP (pre-auth):** 200 requests/minute\n- **PDF generation:** 1 request/minute per token\n\nStandard headers: `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`\n\n## Response Format\n\n**Success:**\n```json\n{ \"success\": true, \"data\": { ... }, \"pagination\": { ... } }\n```\n\n**Error:**\n```json\n{ \"success\": false, \"error\": { \"code\": \"ERROR_CODE\", \"message\": \"Human-readable message\" } }\n```\n\n## Time windows and date filters\n\nThere is **no global date-filter convention**. Each endpoint documents its own parameters:\n\n| Endpoint family | Parameter | Allowed values |\n|-----------------|-----------|------------------|\n| Test runs list | `start_date`, `end_date` | RFC3339 timestamps (both required together) |\n| Explorer, specs, analytics | `days` | Integer snapped to **7, 30, or 90** (see each endpoint) |\n| Test runs list | — | No preset `period` / `dateRange` params |\n| Dashboard, filters | — | **No query params** (fixed snapshot) |\n\n## Served by\n",
    "contact": {
      "name": "TestDino Support",
      "url": "https://docs.testdino.com"
    }
  },
  "servers": [
    {
      "url": "https://api.testdino.com/api/v1/public",
      "description": "Production"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Token Info",
      "description": "Token introspection and project metadata"
    },
    {
      "name": "Test Runs",
      "description": "List, inspect, and drill into test runs"
    },
    {
      "name": "Test Cases",
      "description": "Automated test case details and history"
    },
    {
      "name": "Specs",
      "description": "Project-level spec file health"
    },
    {
      "name": "Manual Tests",
      "description": "Manual test suites and cases (read-only)"
    },
    {
      "name": "Test Case Explorer",
      "description": "Aggregated test case metrics explorer"
    },
    {
      "name": "Context",
      "description": "Debugging payload at three levels (run / suite / test) — error, steps, failure window, attempts, attachments, trace, deep links, and a copy-paste retry command. Designed for AI agents and CI scripts."
    },
    {
      "name": "Dashboard",
      "description": "Project health overview"
    },
    {
      "name": "Filters",
      "description": "Available filter values (environments, branches, developers, tags)"
    },
    {
      "name": "Reports",
      "description": "PDF report generation and download"
    },
    {
      "name": "Analytics",
      "description": "Consolidated analytics summary and test case execution performance"
    },
    {
      "name": "Webhooks",
      "description": "Outbound webhook subscriptions and delivery history"
    },
    {
      "name": "Usage",
      "description": "Subscription usage and limits"
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "User PAT (td_pat_) scoped to the target project"
      }
    },
    "schemas": {
      "Pagination": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "hasNext": {
            "type": "boolean"
          },
          "hasPrev": {
            "type": "boolean"
          }
        }
      },
      "SuccessEnvelope": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "data": {
            "type": "object"
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Classification": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Test case status",
            "example": "active",
            "enum": [
              "active",
              "draft",
              "deprecated"
            ]
          },
          "severity": {
            "type": "string",
            "description": "Severity level",
            "example": "critical",
            "enum": [
              "blocker",
              "critical",
              "major",
              "normal",
              "minor",
              "trivial",
              "not_set"
            ]
          },
          "priority": {
            "type": "string",
            "description": "Priority level",
            "example": "high",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "not_set"
            ]
          },
          "type": {
            "type": "string",
            "description": "Test type",
            "example": "functional",
            "enum": [
              "smoke",
              "regression",
              "functional",
              "integration",
              "e2e",
              "api",
              "unit",
              "performance",
              "security",
              "accessibility",
              "usability",
              "compatibility",
              "acceptance",
              "exploratory",
              "other"
            ]
          },
          "layer": {
            "type": "string",
            "description": "Test layer",
            "example": "e2e",
            "enum": [
              "e2e",
              "api",
              "unit",
              "not_set"
            ]
          },
          "behavior": {
            "type": "string",
            "description": "Expected behavior type",
            "example": "positive",
            "enum": [
              "positive",
              "negative",
              "destructive",
              "not_set"
            ]
          }
        }
      },
      "Automation": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Automation status",
            "example": "manual",
            "enum": [
              "manual",
              "automated"
            ]
          },
          "isFlaky": {
            "type": "boolean"
          },
          "isMuted": {
            "type": "boolean"
          }
        }
      },
      "ClassicStep": {
        "type": "object",
        "properties": {
          "step": {
            "type": "integer",
            "description": "Step number"
          },
          "action": {
            "type": "string",
            "description": "Action to perform",
            "maxLength": 5000
          },
          "data": {
            "type": "string",
            "description": "Test data for this step",
            "maxLength": 5000
          },
          "expectedResult": {
            "type": "string",
            "description": "Expected result after performing the action",
            "maxLength": 5000
          },
          "subSteps": {
            "type": "array",
            "description": "Nested sub-steps (up to 5 levels deep)",
            "items": {
              "$ref": "#/components/schemas/ClassicStep"
            }
          }
        }
      },
      "GherkinStep": {
        "type": "object",
        "properties": {
          "step": {
            "type": "integer",
            "description": "Step number"
          },
          "keyword": {
            "type": "string",
            "enum": [
              "Given",
              "When",
              "Then",
              "And",
              "But"
            ]
          },
          "text": {
            "type": "string",
            "description": "Step description",
            "maxLength": 5000
          },
          "subSteps": {
            "type": "array",
            "description": "Nested sub-steps (up to 5 levels deep)",
            "items": {
              "$ref": "#/components/schemas/GherkinStep"
            }
          }
        }
      },
      "Steps": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "classic",
              "gherkin"
            ],
            "description": "Step format — `classic` uses action/data/expectedResult, `gherkin` uses Given/When/Then keywords."
          },
          "classic": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ClassicStep"
            }
          },
          "gherkin": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GherkinStep"
            }
          }
        }
      },
      "LinkedTest": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "fullTitle": {
            "type": "string",
            "description": "Full title of the linked automated test case"
          },
          "displayTitle": {
            "type": "string",
            "nullable": true
          },
          "linkedAt": {
            "type": "string",
            "format": "date-time"
          },
          "source": {
            "type": "string",
            "enum": [
              "manual_link",
              "auto_created"
            ]
          }
        }
      },
      "Attachment": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "fileName": {
            "type": "string"
          },
          "originalFileName": {
            "type": "string"
          },
          "fileSize": {
            "type": "integer"
          },
          "mimeType": {
            "type": "string"
          },
          "blobUrl": {
            "type": "string"
          },
          "uploadedAt": {
            "type": "string",
            "format": "date-time"
          },
          "stepRef": {
            "type": "string",
            "nullable": true,
            "description": "Dot-separated index path linking the attachment to a step (e.g. `0`, `1.2`)"
          }
        }
      },
      "PopulatedUser": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "CollapsedUser": {
        "type": "object",
        "nullable": true,
        "description": "User reference collapsed to display name and email for public responses.",
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "ManualTestCaseListItem": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "tcm_tc_6641a1b2c3d4e5f6a7b8c9d0"
          },
          "caseId": {
            "type": "string",
            "example": "TC-1"
          },
          "title": {
            "type": "string",
            "example": "Verify login with valid credentials"
          },
          "description": {
            "type": "string"
          },
          "preconditions": {
            "type": "string"
          },
          "postconditions": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "description": "Public-shaped: internal `project` and `createdBy` are stripped; `lastModifiedBy` is collapsed to name + email.",
            "properties": {
              "suite": {
                "type": "object",
                "nullable": true,
                "description": "Populated suite (name only)",
                "properties": {
                  "_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              "lastModifiedBy": {
                "$ref": "#/components/schemas/CollapsedUser"
              }
            }
          },
          "classification": {
            "$ref": "#/components/schemas/Classification"
          },
          "automation": {
            "$ref": "#/components/schemas/Automation"
          },
          "steps": {
            "$ref": "#/components/schemas/Steps"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "customFields": {
            "type": "object",
            "description": "Key-value pairs defined by project-level custom field definitions",
            "additionalProperties": true
          },
          "linkedTests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkedTest"
            }
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ManualTestSuiteListItem": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "tcm_suite_6641a1b2c3d4e5f6a7b8c9d0"
          },
          "name": {
            "type": "string",
            "example": "Login Tests"
          },
          "description": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "description": "Public-shaped: internal `project`, `createdBy`, and `createdAt` are stripped; `lastModifiedBy` is collapsed to name + email.",
            "properties": {
              "lastModifiedBy": {
                "$ref": "#/components/schemas/CollapsedUser"
              }
            }
          },
          "hierarchy": {
            "type": "object",
            "properties": {
              "parentSuite": {
                "type": "object",
                "nullable": true,
                "description": "Populated parent suite (name only), null for root suites",
                "properties": {
                  "_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              "order": {
                "type": "integer",
                "description": "Display order within the parent"
              },
              "depth": {
                "type": "integer",
                "description": "Nesting depth (1 = root, max 6)"
              }
            }
          },
          "childSuitesCount": {
            "type": "integer",
            "description": "Number of direct child suites"
          },
          "children": {
            "type": "array",
            "description": "Nested child suites (same shape, recursively public-shaped).",
            "items": {
              "$ref": "#/components/schemas/ManualTestSuiteListItem"
            }
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TestStats": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "passed": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "skipped": {
            "type": "integer"
          },
          "flaky": {
            "type": "integer"
          },
          "timedOut": {
            "type": "integer"
          },
          "totalAttempts": {
            "type": "integer"
          },
          "retriedTests": {
            "type": "integer"
          }
        }
      },
      "GitMetadata": {
        "type": "object",
        "properties": {
          "branch": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "commit": {
            "type": "object",
            "properties": {
              "hash": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "author": {
                "type": "string"
              }
            }
          }
        }
      },
      "TestRunListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "counter": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "passed",
              "failed",
              "interrupted",
              "incomplete",
              "running"
            ]
          },
          "startTime": {
            "type": "string",
            "format": "date-time"
          },
          "endTime": {
            "type": "string",
            "format": "date-time"
          },
          "duration": {
            "type": "integer"
          },
          "testStats": {
            "$ref": "#/components/schemas/TestStats"
          },
          "metadata": {
            "type": "object",
            "properties": {
              "git": {
                "$ref": "#/components/schemas/GitMetadata"
              }
            }
          },
          "url": {
            "type": "string",
            "description": "Deep-link to the test run in the TestDino UI"
          }
        }
      },
      "TestRunDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/TestRunListItem"
          },
          {
            "type": "object",
            "properties": {
              "errorCategory": {
                "type": "object"
              },
              "liveStats": {
                "type": "object"
              },
              "tagStats": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "errorGroups": {
                "type": "array",
                "description": "Grouped error failures, when present.",
                "items": {
                  "type": "object"
                }
              },
              "coverage": {
                "type": "object",
                "nullable": true,
                "description": "Code coverage metrics, when present."
              },
              "specs": {
                "type": "array",
                "description": "Spec files with flattened test cases, when present.",
                "items": {
                  "type": "object"
                }
              },
              "artifacts": {
                "type": "object",
                "nullable": true,
                "description": "Artifact download URLs, when present."
              }
            }
          }
        ]
      },
      "CreateManualTestSuiteRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "maxLength": 2000
          },
          "parentSuiteId": {
            "type": "string",
            "description": "Nest under an existing suite. Omit for a top-level suite."
          }
        }
      },
      "CreateManualTestCaseRequest": {
        "type": "object",
        "required": [
          "title",
          "suiteName"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "suiteName": {
            "type": "string",
            "maxLength": 255,
            "description": "Exact target suite name (case-sensitive). List suites first to get it."
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Project-configured classification status."
          },
          "testStepsDeclarationType": {
            "type": "string",
            "enum": [
              "Classic",
              "Gherkin"
            ]
          },
          "preconditions": {
            "type": "string"
          },
          "postconditions": {
            "type": "string"
          },
          "steps": {
            "type": "array",
            "maxItems": 100,
            "description": "Classic ({action, expectedResult, data}) or Gherkin ({event, stepDescription}) steps. Per-step attachments are not supported in v1.",
            "items": {
              "type": "object"
            }
          },
          "priority": {
            "type": "string"
          },
          "severity": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "layer": {
            "type": "string"
          },
          "behavior": {
            "type": "string"
          },
          "automationStatus": {
            "type": "string"
          },
          "tags": {
            "type": "string",
            "description": "Comma-separated tags."
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "To be Automated",
                "Is flaky",
                "Muted"
              ]
            }
          },
          "customFields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Key-value strings. Keys must not start with `$` or contain `.`."
          }
        }
      },
      "UpdateManualTestCaseRequest": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "description": "Send only the fields to change. At least one is required.",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "name": {
            "type": "string",
            "maxLength": 500,
            "description": "Alias for title."
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "testStepsDeclarationType": {
            "type": "string",
            "enum": [
              "Classic",
              "Gherkin"
            ]
          },
          "preconditions": {
            "type": "string"
          },
          "postconditions": {
            "type": "string"
          },
          "steps": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object"
            },
            "description": "Full replacement of the case's steps."
          },
          "priority": {
            "type": "string"
          },
          "severity": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "layer": {
            "type": "string"
          },
          "behavior": {
            "type": "string"
          },
          "automationStatus": {
            "type": "string"
          },
          "tags": {
            "type": "string"
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "To be Automated",
                "Is flaky",
                "Muted"
              ]
            }
          },
          "customFields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "comments": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string"
            },
            "description": "Each string is appended as a new comment."
          },
          "issues": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "string"
            },
            "description": "Jira ticket keys to link (e.g. PROJ-123)."
          }
        }
      },
      "CreateReleaseRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "description": {
            "type": "string"
          },
          "note": {
            "type": "string",
            "description": "Rich HTML note."
          },
          "type": {
            "type": "string",
            "description": "Project-configured release type (e.g. release, sprint)."
          },
          "parentReleaseId": {
            "type": "string",
            "description": "Nest under a parent release (max 3 levels)."
          },
          "startDate": {
            "type": "string",
            "description": "ISO date."
          },
          "endDate": {
            "type": "string",
            "description": "ISO date."
          },
          "startedAt": {
            "type": "string",
            "description": "ISO datetime."
          },
          "completedAt": {
            "type": "string",
            "description": "ISO datetime."
          },
          "isStarted": {
            "type": "boolean"
          },
          "isCompleted": {
            "type": "boolean"
          },
          "branch": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "linkedIssues": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "buildTarget": {
            "type": "object",
            "description": "{ platform: web|ios|android|api, version, buildNumber, source, deployUrl }."
          },
          "testers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "User _ids (must be org members)."
          }
        }
      },
      "UpdateReleaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateReleaseRequest"
          },
          {
            "type": "object",
            "minProperties": 1,
            "description": "Send only the fields to change. `name` is optional here."
          }
        ]
      },
      "CreateSessionRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "mission": {
            "type": "string",
            "description": "Rich HTML charter."
          },
          "sessionType": {
            "type": "string"
          },
          "config": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "releaseId": {
            "type": "string"
          },
          "assigneeUserId": {
            "type": "string",
            "description": "User _id or email."
          },
          "state": {
            "type": "string",
            "description": "Project-configured workflow state."
          },
          "estimate": {
            "type": "integer",
            "minimum": 0,
            "description": "Estimate in minutes."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "JSON array of strings."
          },
          "linkedIssues": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "UpdateSessionRequest": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "description": "Send only the fields to change. `status: closed` closes the session.",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "mission": {
            "type": "string"
          },
          "sessionType": {
            "type": "string"
          },
          "config": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "releaseId": {
            "type": "string"
          },
          "assigneeUserId": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "estimate": {
            "type": "integer",
            "minimum": 0
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "linkedIssues": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "status": {
            "type": "string",
            "description": "Set to `closed` to close the session."
          }
        }
      },
      "CreateManualRunRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "note": {
            "type": "string",
            "description": "Rich HTML note."
          },
          "environment": {
            "type": "string"
          },
          "releaseId": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "selectionMode": {
            "type": "string",
            "enum": [
              "all",
              "selected"
            ],
            "description": "`all` (default) includes every case; `selected` uses testCaseIds/suiteIds."
          },
          "testCaseIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "suiteIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "includeUnsorted": {
            "type": "boolean"
          },
          "forecast": {
            "type": "number"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "JSON array of strings."
          },
          "linkedIssues": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "links": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "{ title, url } link objects."
          }
        }
      },
      "UpdateManualRunRequest": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "description": "Send only the fields to change. `status: closed` closes the run.",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "note": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "releaseId": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "selectionMode": {
            "type": "string",
            "enum": [
              "all",
              "selected"
            ]
          },
          "forecast": {
            "type": "number"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "linkedIssues": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "links": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "status": {
            "type": "string",
            "description": "Set to `closed` to close the run."
          }
        }
      },
      "UpdateRunTestCaseRequest": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "description": "Two mutually exclusive modes (mixing them is rejected):\nquick verdict (assigneeUserId and/or result/status/elapsed) or detailed\nresult (comment/linkedIssues/stepResults).\n",
        "properties": {
          "assigneeUserId": {
            "type": "string",
            "nullable": true,
            "description": "User _id or email; `null` unassigns."
          },
          "result": {
            "type": "string",
            "description": "untested | passed | failed | blocked | skipped | retest (display or canonical)."
          },
          "status": {
            "type": "string",
            "description": "Alias for result."
          },
          "elapsed": {
            "type": "number",
            "minimum": 0,
            "description": "Seconds."
          },
          "comment": {
            "type": "string",
            "description": "Rich HTML."
          },
          "linkedIssues": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "stepResults": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "e.g. [{ order: 1, status: passed, comment: '' }]."
          }
        }
      },
      "WebhookSubscription": {
        "type": "object",
        "description": "An outbound webhook subscription. The signing secret is never included.",
        "properties": {
          "_id": {
            "type": "string",
            "example": "wh_8f2c"
          },
          "projectId": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "RUN_STARTED",
                "RUN_FINISHED"
              ]
            }
          },
          "conditions": {
            "type": "object",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "any",
                  "failed",
                  "passed"
                ],
                "description": "RUN_FINISHED filter"
              }
            }
          },
          "active": {
            "type": "boolean"
          },
          "description": {
            "type": "string",
            "maxLength": 200
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "n8n",
              "zapier"
            ]
          },
          "consecutiveFailures": {
            "type": "integer",
            "description": "Consecutive failed delivery attempts"
          },
          "disabledReason": {
            "type": "string",
            "nullable": true
          },
          "lastDeliveryAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "RUN_STARTED",
                "RUN_FINISHED"
              ]
            }
          },
          "conditions": {
            "type": "object",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "any",
                  "failed",
                  "passed"
                ]
              }
            }
          },
          "description": {
            "type": "string",
            "maxLength": 200
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "n8n",
              "zapier"
            ]
          }
        }
      },
      "WebhookUpdate": {
        "type": "object",
        "description": "Partial update. At least one field is required.",
        "minProperties": 1,
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "RUN_STARTED",
                "RUN_FINISHED"
              ]
            }
          },
          "conditions": {
            "type": "object",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "any",
                  "failed",
                  "passed"
                ]
              }
            }
          },
          "active": {
            "type": "boolean"
          },
          "description": {
            "type": "string",
            "maxLength": 200
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "description": "A delivery attempt record. The request payload is excluded from list views.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Delivery identifier, sent as the X-TestDino-Delivery header"
          },
          "webhookId": {
            "type": "string"
          },
          "event": {
            "type": "string",
            "enum": [
              "RUN_STARTED",
              "RUN_FINISHED"
            ]
          },
          "runId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "success",
              "failed"
            ]
          },
          "attempt": {
            "type": "integer"
          },
          "nextAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "statusCode": {
            "type": "integer",
            "nullable": true
          },
          "responseMs": {
            "type": "integer",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookPagination": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "hasMore": {
            "type": "boolean"
          }
        }
      }
    },
    "parameters": {
      "projectId": {
        "name": "projectId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The project identifier (e.g. `project_abc123`). Must match the project associated with your PAT."
      },
      "page": {
        "name": "page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        },
        "description": "Page number for paginated results. Starts at 1."
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "enum": [
            10,
            25,
            50,
            100
          ],
          "default": 10
        },
        "description": "Page size. Must be one of `10`, `25`, `50`, or `100` (default `10`). Any other value returns `400 INVALID_LIMIT`."
      },
      "include": {
        "name": "include",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Comma-separated list of extra data to include (e.g. `errors,coverage`). Use `all` for everything available on this endpoint."
      },
      "dateRange": {
        "name": "dateRange",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "7d",
            "30d",
            "90d"
          ]
        },
        "description": "Predefined date range filter. **Takes precedence** over `startDate`/`endDate` if both are provided. For custom ranges, omit this and use `startDate`+`endDate` instead."
      },
      "environment": {
        "name": "environment",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Filter results by environment name (e.g. `production`, `staging`). Use the `/filters` endpoint to discover available environments."
      },
      "startDate": {
        "name": "startDate",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "Start of a custom date range (ISO 8601 date, e.g. `2026-03-01`). Must be used together with `endDate`. Ignored if `dateRange` is also provided."
      },
      "endDate": {
        "name": "endDate",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "End of a custom date range (ISO 8601 date, e.g. `2026-03-31`). Must be used together with `startDate`. Ignored if `dateRange` is also provided."
      },
      "idempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Optional but recommended on create (`POST`) requests. Send a unique value (e.g. a UUID) per\nlogical create. A retry with the same key within 24h replays the original response instead of\ncreating a duplicate. Same key with a different body returns `422 IDEMPOTENCY_KEY_REUSE`; a key\nfor a request still in flight returns `409`. Omitting the header is allowed (no deduplication).\n"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, invalid, expired, or revoked token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "success": false,
              "error": {
                "code": "UNAUTHORIZED",
                "message": "Invalid or unknown user PAT"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Token not authorized for this project",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "RateLimit-Limit": {
            "description": "Max requests allowed in the current window",
            "schema": {
              "type": "integer",
              "example": 100
            }
          },
          "RateLimit-Remaining": {
            "description": "Requests remaining in the current window",
            "schema": {
              "type": "integer",
              "example": 0
            }
          },
          "RateLimit-Reset": {
            "description": "Seconds until the current window resets",
            "schema": {
              "type": "integer",
              "example": 42
            }
          },
          "Retry-After": {
            "description": "Seconds to wait before retrying",
            "schema": {
              "type": "integer",
              "example": 60
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "success": false,
              "error": {
                "code": "RATE_LIMIT_EXCEEDED",
                "message": "Too many requests"
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Invalid request parameters",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/{projectId}/token-info": {
      "get": {
        "operationId": "verifyToken",
        "tags": [
          "Token Info"
        ],
        "summary": "Verify token and retrieve token metadata",
        "description": "Returns metadata about the authenticated PAT token, including its scopes, expiry, and current\nrate limit quotas. User PATs can be scoped to multiple organization/project pairs, so `scopes`\nis the source of truth for project access.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "Token metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "pat",
                            "api_key"
                          ]
                        },
                        "scopes": {
                          "description": "A permission string (e.g. `runs:read`) or an org scope. Each org scope lists its projects as `{ id, name }` objects (the Token Registry resolves names server-side); a wildcard scope uses the literal `*` and carries no project names.\n",
                          "type": "array",
                          "items": {
                            "oneOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "orgId": {
                                    "type": "string"
                                  },
                                  "projects": {
                                    "oneOf": [
                                      {
                                        "type": "string",
                                        "enum": [
                                          "*"
                                        ]
                                      },
                                      {
                                        "type": "array",
                                        "items": {
                                          "type": "object",
                                          "properties": {
                                            "id": {
                                              "type": "string"
                                            },
                                            "name": {
                                              "type": "string",
                                              "nullable": true
                                            }
                                          },
                                          "required": [
                                            "id",
                                            "name"
                                          ]
                                        }
                                      }
                                    ]
                                  }
                                },
                                "required": [
                                  "orgId",
                                  "projects"
                                ]
                              }
                            ]
                          }
                        },
                        "projectName": {
                          "description": "Human name of the path `projectId`, resolved from the token's scopes. Null for a wildcard scope or a project the token is not scoped to.\n",
                          "type": "string",
                          "nullable": true
                        },
                        "expiresAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "createdAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "lastUsed": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "usageCount": {
                          "type": "integer",
                          "nullable": true
                        },
                        "rateLimit": {
                          "type": "object",
                          "properties": {
                            "limit": {
                              "type": "integer"
                            },
                            "window": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/test-runs": {
      "get": {
        "operationId": "listTestRuns",
        "tags": [
          "Test Runs"
        ],
        "summary": "List test runs with filtering and pagination",
        "description": "Returns a paginated list of test runs for the project. Default sort is **`counter_desc`**\n(newest counter first). Query parameter names are listed below\n(snake_case).\n\n**Date range:** pass **`start_date`** and **`end_date`** together as RFC3339 timestamps\n(e.g. `2026-03-01T00:00:00Z`). There is no `period` or camelCase date alias.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "passed",
                "failed",
                "interrupted",
                "incomplete",
                "running"
              ]
            },
            "description": "Filter by run status. CSV allowed (e.g. `failed,interrupted`)."
          },
          {
            "name": "branch",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by git branch (CSV allowed)."
          },
          {
            "name": "environment",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by environment name (must exist for this project)."
          },
          {
            "name": "author",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by git commit author (CSV allowed)."
          },
          {
            "name": "run_tags",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated run-level tags (e.g. `smoke,prod`)."
          },
          {
            "name": "test_case_tags",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated test-case tags that must appear on cases in the run."
          },
          {
            "name": "tag_match",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "exact"
              ]
            },
            "description": "When set to `exact`, tag filters require an exact tag match."
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Free-text search across commit messages and run counter numbers."
          },
          {
            "name": "start_date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of run time window (RFC3339). Must be used with `end_date`."
          },
          {
            "name": "end_date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of run time window (RFC3339). Must be used with `start_date`."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "counter_desc",
                "counter_asc",
                "duration_asc",
                "duration_desc"
              ],
              "default": "counter_desc"
            },
            "description": "Sort order for the run list."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of test runs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TestRunListItem"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/test-runs/{runId}": {
      "get": {
        "operationId": "getTestRun",
        "tags": [
          "Test Runs"
        ],
        "summary": "Get test run details",
        "description": "Returns detailed information about a single test run, including test stats (pass/fail/skip/flaky counts),\ngit metadata (branch, commit, PR), tag stats, error categorization, and a deep-link URL to the TestDino UI.\n\n**No query parameters.** Optional `include` sections from the previous API are not supported on this route.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The test run identifier (e.g. `test_run_abc123`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Test run detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/TestRunDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/{projectId}/test-cases/{caseId}": {
      "get": {
        "operationId": "getTestCase",
        "tags": [
          "Test Cases"
        ],
        "summary": "Get latest test case details",
        "description": "Returns the latest detailed execution payload for a single automated test case.\n`caseId` is the Playwright `pw_test_id`. It resolves to the latest\nmatching run, optionally scoped by either `branch` or `environment`, and returns\nper-attempt errors, attachments, logs, and recursive steps.\n\n`branch` and `environment` are mutually exclusive. If both are supplied, Data Handler\nreturns a validation error.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "caseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Playwright test identifier / `pw_test_id` (e.g. `8dfe...-a2e2...`)."
          },
          {
            "name": "branch",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Resolve the latest execution of this test on a specific git branch."
          },
          {
            "name": "environment",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Resolve the latest execution of this test in a configured environment. Mutually exclusive with `branch`."
          }
        ],
        "responses": {
          "200": {
            "description": "Test case detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "pw_test_id": {
                          "type": "string"
                        },
                        "suite_id": {
                          "type": "string"
                        },
                        "title": {
                          "type": "string"
                        },
                        "title_path": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "status": {
                          "type": "string"
                        },
                        "platform": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "resolved_run_id": {
                          "type": "string"
                        },
                        "resolved_run_start_time": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "scope_used": {
                          "type": "string",
                          "enum": [
                            "latest",
                            "branch",
                            "environment"
                          ]
                        },
                        "scope_value": {
                          "type": "string",
                          "nullable": true
                        },
                        "url": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/{projectId}/test-cases/history": {
      "get": {
        "operationId": "getTestCaseHistory",
        "tags": [
          "Test Cases"
        ],
        "summary": "Get execution history for a test case by title",
        "description": "Returns the execution history of a test case identified by its visible `title` (the name\nshown in the UI). Internally, the title is resolved to the canonical full title\n(`path/to/spec > describe > title`) by matching the most recently executed test case.\nEach entry represents one execution with its status, duration, retries, platform, branch,\nand associated test run counter.\n\nAlso includes pre-computed summary metrics (execution count, pass/fail/flaky rates, average\nduration), unique errors encountered, and per-platform and per-environment breakdowns.\n\nResults are ordered by test run start time (newest first). Use `offset` and `limit` for pagination.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "title",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The test case title as shown in the UI (e.g. `should display login form`). URL-encode if it contains special characters. If multiple tests share the same title, the most recently executed one is used — inspect `data.testCase.fullTitle` in the response to see which was resolved."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Number of history entries to return (1–100, default 20). Upstream fetch limit may snap to 50, 100, 200, or 500 before client-side pagination."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Number of entries to skip (for pagination). Default 0."
          }
        ],
        "responses": {
          "200": {
            "description": "Execution history with summary and breakdown metrics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "testCase": {
                          "type": "object",
                          "properties": {
                            "title": {
                              "type": "string",
                              "description": "Short test case title"
                            },
                            "fullTitle": {
                              "type": "string",
                              "description": "Full title including parent suites"
                            },
                            "specName": {
                              "type": "string",
                              "description": "Spec file path"
                            }
                          }
                        },
                        "summary": {
                          "type": "object",
                          "description": "Pre-computed aggregate metrics",
                          "properties": {
                            "executions": {
                              "type": "integer"
                            },
                            "passedRuns": {
                              "type": "integer"
                            },
                            "failedRuns": {
                              "type": "integer"
                            },
                            "flakyRuns": {
                              "type": "integer"
                            },
                            "skippedRuns": {
                              "type": "integer"
                            },
                            "successRate": {
                              "type": "number"
                            },
                            "failureRate": {
                              "type": "number"
                            },
                            "flakyRate": {
                              "type": "number"
                            },
                            "avgDuration": {
                              "type": "number"
                            },
                            "minDuration": {
                              "type": "number"
                            },
                            "maxDuration": {
                              "type": "number"
                            },
                            "platforms": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "history": {
                          "type": "array",
                          "description": "Paginated list of executions (newest first)",
                          "items": {
                            "type": "object",
                            "properties": {
                              "testRunId": {
                                "type": "string"
                              },
                              "testCaseId": {
                                "type": "string"
                              },
                              "counter": {
                                "type": "integer"
                              },
                              "branch": {
                                "type": "string"
                              },
                              "platform": {
                                "type": "string"
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "passed",
                                  "failed",
                                  "flaky",
                                  "skipped"
                                ]
                              },
                              "duration": {
                                "type": "number"
                              },
                              "retries": {
                                "type": "integer"
                              },
                              "timestamp": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "attemptsCount": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "uniqueErrors": {
                          "type": "array",
                          "description": "Distinct error messages encountered (max 20)",
                          "items": {
                            "type": "object"
                          }
                        },
                        "platformMetrics": {
                          "type": "array",
                          "description": "Per-platform execution stats",
                          "items": {
                            "type": "object",
                            "properties": {
                              "platform": {
                                "type": "string"
                              },
                              "executions": {
                                "type": "integer"
                              },
                              "failureCount": {
                                "type": "integer"
                              },
                              "failureRate": {
                                "type": "number"
                              },
                              "flakyCount": {
                                "type": "integer"
                              },
                              "flakyRate": {
                                "type": "number"
                              },
                              "avgDuration": {
                                "type": "number"
                              }
                            }
                          }
                        },
                        "environmentMetrics": {
                          "type": "array",
                          "description": "Per-environment execution stats",
                          "items": {
                            "type": "object",
                            "properties": {
                              "environment": {
                                "type": "string"
                              },
                              "executions": {
                                "type": "integer"
                              },
                              "failureRate": {
                                "type": "number"
                              },
                              "flakyRate": {
                                "type": "number"
                              },
                              "avgDuration": {
                                "type": "number"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid title parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No test case found with the given title",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/{projectId}/context": {
      "get": {
        "operationId": "getFailureContext",
        "tags": [
          "Context"
        ],
        "summary": "Get debugging context for a run, suite, or test case",
        "description": "Returns debugging context at run, suite, or test level.\nPass `runId`, `suiteId` + `runId`, or `caseId` (+ optional `runId`).\nUse `format=md` for Markdown output.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "runId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Run ID. Required for run level and required with `suiteId`."
          },
          {
            "name": "suiteId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Suite ID. Requires `runId`."
          },
          {
            "name": "caseId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Test case ID. `runId` is optional."
          },
          {
            "name": "attempt",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Zero-indexed attempt number (test level only)."
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "md"
              ],
              "default": "json"
            },
            "description": "Response format: `json` (default) or `md`."
          },
          {
            "name": "detail",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "default",
                "compact",
                "summary"
              ],
              "default": "default"
            },
            "description": "Payload size mode: `default`, `summary`, or `compact`."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            },
            "description": "Pagination (run + suite levels only)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 50
            },
            "description": "Pagination size for run/suite levels."
          },
          {
            "name": "maxLength",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50000
            },
            "description": "Maximum Markdown length when `format=md`."
          }
        ],
        "responses": {
          "200": {
            "description": "Context payload. Shape depends on `data.level` (`run`, `suite`, `test`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "description": "Shape varies by `level`. See description for per-level fields.",
                      "properties": {
                        "level": {
                          "type": "string",
                          "enum": [
                            "run",
                            "suite",
                            "test"
                          ],
                          "description": "Discriminator — tells the caller which shape they received."
                        },
                        "links": {
                          "type": "object",
                          "description": "Present at every level.",
                          "properties": {
                            "uiUrl": {
                              "type": "string",
                              "description": "Deep-link to the corresponding page in the TestDino UI."
                            },
                            "traceViewerUrl": {
                              "type": "string",
                              "nullable": true,
                              "description": "Test level only. Pre-loaded `trace.playwright.dev` URL. `null` when no trace zip is attached."
                            },
                            "retryCommand": {
                              "type": "string",
                              "description": "Copy-paste `npx playwright test …` command to reproduce locally."
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string",
                  "description": "Markdown document. Returned when `?format=md`."
                }
              }
            }
          },
          "400": {
            "description": "Validation failure. Codes: `VALIDATION_ERROR` (none of `runId`/`suiteId`/`caseId` provided, or bad `format`/`detail`/`maxLength`), `INVALID_ATTEMPT` (attempt index out of range).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Codes: `TEST_RUN_NOT_FOUND`, `TEST_SUITE_NOT_FOUND`, `TEST_CASE_NOT_FOUND`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/specs": {
      "get": {
        "operationId": "listSpecs",
        "tags": [
          "Specs"
        ],
        "summary": "List spec file health metrics",
        "description": "Returns a paginated list of spec files (test files) with aggregated health metrics such as\npass/fail counts, flakiness rate, and average duration. Useful for identifying problematic\nspec files across your test suite over a configurable lookback period.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Page size after spec-file aggregation (1–100, default 10). Not the shared runs-list enum."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "passed",
                "failed",
                "flaky",
                "skipped"
              ]
            },
            "description": "Filter by aggregated spec status."
          },
          {
            "$ref": "#/components/parameters/environment"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search by spec file path or name."
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30,
                90
              ],
              "default": 30
            },
            "description": "Lookback window in days. Values other than 7, 30, or 90 are snapped to the nearest allowed window (default 30 when absent/invalid)."
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "fileName",
                "fullTitle",
                "executions",
                "failureRate",
                "flakyRate",
                "avgDuration",
                "lastExecution"
              ],
              "default": "fileName"
            },
            "description": "Field to sort aggregated spec rows by. `fullTitle` falls back to `fileName` (no per-spec title grain)."
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            },
            "description": "Sort direction: `asc` or `desc` (default `asc`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated spec health list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/manual-test-suites": {
      "get": {
        "operationId": "listManualTestSuites",
        "tags": [
          "Manual Tests"
        ],
        "summary": "List manual test suites",
        "description": "Returns a flat list of manual test suites for one hierarchy level. Use `parentSuiteId`\nto navigate into child suites; omit it for top-level suites. Response is `{ data, count }`\n(not a paginated envelope).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "parentSuiteId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter to children of this suite. Omit for top-level suites. Pass the literal string `null` to scope to root-level suites explicitly."
          }
        ],
        "responses": {
          "200": {
            "description": "Suite list with count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ManualTestSuiteListItem"
                      }
                    },
                    "count": {
                      "type": "integer",
                      "description": "Total suites returned in `data`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createManualTestSuite",
        "tags": [
          "Manual Tests"
        ],
        "summary": "Create a manual test suite",
        "description": "Creates a manual test suite. Requires a PAT whose owner has a writer role\n(owner/admin/member) on the project's organization; a viewer-role token gets `403`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateManualTestSuiteRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Suite created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/manual-test-cases": {
      "get": {
        "operationId": "listManualTestCases",
        "tags": [
          "Manual Tests"
        ],
        "summary": "List manual test cases with filtering",
        "description": "Returns a filterable list of manual test cases (single page). Supports classification\nand automation filters, suite, and tags. **No `page` or sort params**: the response is one\npage ordered by `createdAt` desc.\n\nSearch matches title and caseId only (case-insensitive).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            },
            "description": "Max cases to return (1–1000, default 100)."
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Free-text search across title and caseId."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "draft",
                "deprecated"
              ]
            },
            "description": "Filter by classification status."
          },
          {
            "name": "severity",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "blocker",
                "critical",
                "major",
                "normal",
                "minor",
                "trivial",
                "not_set"
              ]
            },
            "description": "Filter by severity level."
          },
          {
            "name": "priority",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low",
                "not_set"
              ]
            },
            "description": "Filter by priority."
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "smoke",
                "regression",
                "functional",
                "integration",
                "e2e",
                "api",
                "unit",
                "performance",
                "security",
                "accessibility",
                "usability",
                "compatibility",
                "acceptance",
                "exploratory",
                "other"
              ]
            },
            "description": "Filter by test type."
          },
          {
            "name": "layer",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "e2e",
                "api",
                "unit",
                "not_set"
              ]
            },
            "description": "Filter by test layer."
          },
          {
            "name": "behavior",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "positive",
                "negative",
                "destructive",
                "not_set"
              ]
            },
            "description": "Filter by expected behavior type."
          },
          {
            "name": "automationStatus",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "manual",
                "automated"
              ]
            },
            "description": "Filter by automation status."
          },
          {
            "name": "suiteId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter to test cases belonging to this suite. Use `null` for unassigned cases."
          },
          {
            "name": "tags",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated tags to filter by (e.g. `smoke,login`). Returns cases matching any of the tags."
          }
        ],
        "responses": {
          "200": {
            "description": "Manual test cases",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ManualTestCaseListItem"
                      }
                    },
                    "count": {
                      "type": "integer",
                      "description": "Total cases returned in `data`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createManualTestCase",
        "tags": [
          "Manual Tests"
        ],
        "summary": "Create a manual test case",
        "description": "Creates a manual test case in the named suite. Requires a writer-role PAT (viewer → `403`).\nEnum-like fields are validated against the project's configured options. Attachments are not\nsupported in v1.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateManualTestCaseRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Test case created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/manual-test-cases/{caseId}": {
      "get": {
        "operationId": "getManualTestCase",
        "tags": [
          "Manual Tests"
        ],
        "summary": "Get manual test case details",
        "description": "Returns the full details of a single manual test case, including title, description,\npreconditions, postconditions, steps (classic or gherkin with nested sub-steps),\nclassification, automation status, tags, custom fields, linked automated tests,\nand attachments. Internal `metadata.project` and `metadata.createdBy` are stripped;\n`metadata.lastModifiedBy` is collapsed to name and email.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "caseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The manual test case identifier (e.g. `tcm_tc_...`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Manual test case detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ManualTestCaseListItem"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateManualTestCase",
        "tags": [
          "Manual Tests"
        ],
        "summary": "Update a manual test case",
        "description": "Updates a manual test case. Send only the fields to change. `steps` is a full replacement.\n`comments` appends each string as a new comment (retries duplicate — send an Idempotency-Key\nor avoid retrying). Requires a writer-role PAT (viewer → `403`). Attachments not supported in v1.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "caseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Internal `_id` or a counter-style ID like `TC-123`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateManualTestCaseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Test case updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/test-case-explorer": {
      "get": {
        "operationId": "exploreTestCases",
        "tags": [
          "Test Case Explorer"
        ],
        "summary": "Explore test cases with aggregated metrics",
        "description": "Returns a paginated list of test cases with aggregated metrics across multiple runs,\nincluding pass rate, fail rate, flakiness rate, average duration, and last execution time.\nUseful for identifying consistently failing, slow, or flaky tests over a configurable\nlookback period. Filter by spec file, platform, environment, status, or tags.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/page"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                10,
                25,
                50
              ],
              "default": 25
            },
            "description": "Page size. Must be one of `10`, `25`, or `50` (default `25`). Any other value returns `400 INVALID_LIMIT`."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "flaky",
                "chronic",
                "stable"
              ]
            },
            "description": "Filter by behavioral status class. `stable` requires `days=90`."
          },
          {
            "name": "specFilePath",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter to test cases from a specific spec file path."
          },
          {
            "name": "platform",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by test platform (e.g. `chromium`, `firefox`, `webkit`)."
          },
          {
            "$ref": "#/components/parameters/environment"
          },
          {
            "name": "tags",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Single tag string. Not comma-separated multi-tag."
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30,
                90
              ],
              "default": 30
            },
            "description": "Rolling window in days. Must be 7, 30, or 90 (default 30). Other values return 400."
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Free-text search across test case names."
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "suite_file_path",
                "title",
                "flaky_rate",
                "failure_rate",
                "reliability_score",
                "total_runs",
                "p95_duration_ms",
                "duration_trend_slope",
                "consecutive_failure_streak",
                "last_seen_at",
                "last_duration_ms",
                "avg_duration_ms"
              ],
              "default": "suite_file_path"
            },
            "description": "Sort field. Default `suite_file_path`. Sort-dependent default direction applies when `order` is omitted."
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "description": "Sort direction. Default depends on `sortBy`: text sorts default `asc`, metrics default `desc`."
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregated test case data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/dashboard": {
      "get": {
        "operationId": "getDashboard",
        "tags": [
          "Dashboard"
        ],
        "summary": "Project health dashboard",
        "description": "Returns a fixed dashboard snapshot over a **30-day** window. **No query parameters**: date and\nenvironment filters are not supported on this route.\n\nThe response typically contains `{ snapshot, active_runs, data_freshness }`.\nOlder response blocks such as `mostFlakyTests` may be absent.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "Dashboard data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "description": "Dashboard snapshot.",
                      "properties": {
                        "snapshot": {
                          "type": "object",
                          "description": "Project snapshot metrics."
                        },
                        "active_runs": {
                          "type": "array",
                          "description": "Currently running test runs overlay.",
                          "items": {
                            "type": "object"
                          }
                        },
                        "data_freshness": {
                          "type": "object",
                          "description": "Timestamps indicating how fresh the snapshot is."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/filters": {
      "get": {
        "operationId": "getFilterValues",
        "tags": [
          "Filters"
        ],
        "summary": "Get available filter values and tags",
        "description": "Returns facet values for populating filter dropdowns: branches, authors, platforms, tags,\nrun tags, statuses, and environments. **No query parameters**: the response uses a fixed\n**90-day** window (widest facet set).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "Filter values",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "description": "Facet arrays of available filter values.",
                      "properties": {
                        "branches": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "last_branches": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "authors": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "platforms": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "tags": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "run_tags": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "statuses": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "environments": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "ci_provider": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "git_repo": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/reports/pdf": {
      "get": {
        "operationId": "generatePdfReport",
        "tags": [
          "Reports"
        ],
        "summary": "Generate and download a project report PDF",
        "description": "Generates a PDF report summarizing test results for the project over a configurable lookback\nperiod. The response is a binary PDF stream. Rate limited to **1 request per minute** per token.\n\nThe report includes test run summaries, pass/fail trends, flaky tests, and environment breakdowns.\nOptionally filter by branch, environment, or tags to scope the report.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 30,
              "default": 7
            },
            "description": "Lookback period in days (1–30, default 7). Determines how far back the report covers."
          },
          {
            "name": "branch",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter report data to a specific git branch."
          },
          {
            "$ref": "#/components/parameters/environment"
          },
          {
            "name": "tags",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated run tags to filter report data (e.g. `smoke,regression`)."
          }
        ],
        "responses": {
          "200": {
            "description": "PDF binary stream",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string",
                  "example": "attachment; filename=\"TestDino-Report-MyProject-7d-2026-04-10.pdf\""
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "description": "PDF rate limit exceeded (1 per minute)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              }
            }
          }
        }
      }
    },
    "/{projectId}/analytics/summary": {
      "get": {
        "operationId": "getAnalyticsSummary",
        "tags": [
          "Analytics"
        ],
        "summary": "Consolidated analytics summary",
        "description": "Returns a composed analytics summary: top failing\ntests, flaky tests, slowest test cases, and tag stats for one rolling window. Chart payloads\nare stripped.\n\n**Only `days` is supported.** It is snapped to **7, 30, or 90** (default **30** when absent or invalid).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30,
                90
              ],
              "default": 30
            },
            "description": "Rolling window in days. Snapped to 7, 30, or 90 (default 30)."
          }
        ],
        "responses": {
          "200": {
            "description": "Analytics summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "window_days": {
                          "type": "integer",
                          "enum": [
                            7,
                            30,
                            90
                          ],
                          "description": "Effective rolling window used for composition."
                        },
                        "topFailingTests": {
                          "type": "array",
                          "description": "Top failing tests for the window.",
                          "items": {
                            "type": "object"
                          }
                        },
                        "flakyTests": {
                          "type": "array",
                          "description": "Top flaky tests (capped to 5).",
                          "maxItems": 5,
                          "items": {
                            "type": "object"
                          }
                        },
                        "slowestTestCases": {
                          "type": "array",
                          "description": "Slowest test cases (capped to 5).",
                          "maxItems": 5,
                          "items": {
                            "type": "object"
                          }
                        },
                        "tags": {
                          "type": "array",
                          "description": "Tag statistics for the window.",
                          "items": {
                            "type": "object"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/analytics/test-cases/performance": {
      "get": {
        "operationId": "getTestCasePerformance",
        "tags": [
          "Analytics"
        ],
        "summary": "Test case execution performance trends",
        "description": "Returns per-test performance rows composed from slow + failure intelligence endpoints\n(duration percentiles merged with failure stats by `pw_test_id`).\n\n**Only `days` is supported.** Snapped to **7, 30, or 90** (default **30**).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30,
                90
              ],
              "default": 30
            },
            "description": "Rolling window in days. Snapped to 7, 30, or 90 (default 30)."
          }
        ],
        "responses": {
          "200": {
            "description": "Performance trend data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "Usage"
        ],
        "summary": "Subscription usage and per-project breakdown",
        "description": "Returns the organization's subscription plan, overall test case usage (limit, used, remaining),\nthis project's individual allocation, and a per-project usage breakdown across all projects\nin the organization. Also includes the billing period start/end and next reset date.\n\nIf no subscription exists, free-tier defaults are returned (5,000 test cases/month, 1 project).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "responses": {
          "200": {
            "description": "Usage statistics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "planName": {
                          "type": "string"
                        },
                        "orgName": {
                          "type": "string",
                          "nullable": true,
                          "description": "Organization name"
                        },
                        "projects": {
                          "type": "array",
                          "description": "Active projects in this organization",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "orgLimit": {
                          "type": "integer"
                        },
                        "orgUsed": {
                          "type": "integer"
                        },
                        "orgRemaining": {
                          "type": "integer"
                        },
                        "projectUsed": {
                          "type": "integer",
                          "description": "Executions this project has consumed in the current billing window"
                        },
                        "projectUsage": {
                          "type": "array",
                          "description": "Per-project executions breakdown across every project the caller can access in this org",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string",
                                "nullable": true
                              },
                              "used": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "periodStart": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "periodEnd": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "resetDate": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/{projectId}/releases": {
      "get": {
        "operationId": "listReleases",
        "tags": [
          "Releases"
        ],
        "summary": "List releases",
        "description": "Browse releases (milestones). Filter by type, completion, parent, status, or name.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Match by name."
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "isCompleted",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "parentReleaseId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Direct children of this release."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "startDate",
                "endDate",
                "name"
              ]
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "description": "Default 25."
          }
        ],
        "responses": {
          "200": {
            "description": "Release list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createRelease",
        "tags": [
          "Releases"
        ],
        "summary": "Create a release",
        "description": "Creates a release. Requires a writer-role PAT (viewer → `403`).",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateReleaseRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Release created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/releases/{releaseId}": {
      "get": {
        "operationId": "getRelease",
        "description": "Retrieve a single release by internal `_id` or counter-style ID such as `MS-12`.",
        "tags": [
          "Releases"
        ],
        "summary": "Get a release",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "releaseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Internal `_id` or counter-style ID like `MS-12`."
          }
        ],
        "responses": {
          "200": {
            "description": "Release detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateRelease",
        "tags": [
          "Releases"
        ],
        "summary": "Update a release",
        "description": "Send only the fields to change. Requires a writer-role PAT (viewer → `403`).",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "releaseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateReleaseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Release updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/sessions": {
      "get": {
        "operationId": "listSessions",
        "description": "List exploratory testing sessions in a project, with filters for status, state, session type, assignee, release, and tags.",
        "tags": [
          "Sessions"
        ],
        "summary": "List exploratory sessions",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "closed"
              ]
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sessionType",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assigneeUserId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "User _id or email."
          },
          {
            "name": "releaseId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Sessions in this release; `none` for unlinked."
          },
          {
            "name": "tags",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated tags."
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "updatedAt",
                "name"
              ]
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "description": "Default 25."
          }
        ],
        "responses": {
          "200": {
            "description": "Session list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createSession",
        "tags": [
          "Sessions"
        ],
        "summary": "Create an exploratory session",
        "description": "Creates a session. Requires a writer-role PAT (viewer → `403`). Attachments not supported in v1.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSessionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/sessions/{sessionId}": {
      "get": {
        "operationId": "getSession",
        "description": "Retrieve a single exploratory session by internal `_id` or counter-style ID such as `SES-12`.",
        "tags": [
          "Sessions"
        ],
        "summary": "Get an exploratory session",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Internal `_id` or counter-style ID like `SES-12`."
          }
        ],
        "responses": {
          "200": {
            "description": "Session detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateSession",
        "tags": [
          "Sessions"
        ],
        "summary": "Update an exploratory session",
        "description": "Send only the fields to change. `status: closed` closes the session. Requires a writer-role PAT.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSessionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/manual-runs": {
      "get": {
        "operationId": "listManualRuns",
        "description": "List manual test runs in a project, with filters for status, state, environment, release, and tags.",
        "tags": [
          "Manual Runs"
        ],
        "summary": "List manual test runs",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "closed"
              ]
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "environment",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "releaseId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Runs in this release; `none` for unlinked."
          },
          {
            "name": "tags",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated tags."
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "updatedAt",
                "name"
              ]
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "description": "Default 25."
          }
        ],
        "responses": {
          "200": {
            "description": "Run list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createManualRun",
        "tags": [
          "Manual Runs"
        ],
        "summary": "Create a manual test run",
        "description": "Creates a run. `selectionMode: all` (default) includes every case in the project and can\ncreate thousands of per-case records — this endpoint has a tighter rate limit than other\nwrites. Requires a writer-role PAT (viewer → `403`). Attachments not supported in v1.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateManualRunRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Run created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/manual-runs/{runId}": {
      "get": {
        "operationId": "getManualRun",
        "description": "Retrieve a single manual test run by internal `_id` or counter-style ID such as `RUN-12`.",
        "tags": [
          "Manual Runs"
        ],
        "summary": "Get a manual test run",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Internal `_id` or counter-style ID like `RUN-12`."
          }
        ],
        "responses": {
          "200": {
            "description": "Run detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateManualRun",
        "tags": [
          "Manual Runs"
        ],
        "summary": "Update a manual test run",
        "description": "Send only the fields to change. `status: closed` closes the run. Requires a writer-role PAT.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateManualRunRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Run updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/manual-runs/{runId}/test-cases": {
      "get": {
        "operationId": "listManualRunTestCases",
        "tags": [
          "Manual Runs"
        ],
        "summary": "List per-case records in a run",
        "description": "The rows in a run's test-case table — case identity, assignee, and current result.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Match by case title or caseKey."
          },
          {
            "name": "assignee",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "User _id or email."
          },
          {
            "name": "result",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by result/status."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Alias for result."
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "updatedAt",
                "status",
                "caseKey"
              ]
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "description": "Default 25."
          }
        ],
        "responses": {
          "200": {
            "description": "Per-case record list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/{projectId}/manual-runs/{runId}/test-cases/{runCaseId}": {
      "patch": {
        "operationId": "updateManualRunTestCase",
        "tags": [
          "Manual Runs"
        ],
        "summary": "Record a verdict or detailed result for a case in a run",
        "description": "Quick verdict (assignee and/or result) or detailed result (comment/linkedIssues/stepResults) —\nthe two modes cannot be combined in one call. Requires a writer-role PAT. Attachments not\nsupported in v1. Closed runs reject result writes.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "runCaseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Per-case reference: `tcm_rtc_...` ID, caseKey (`TC-156`), or the case `_id`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateRunTestCaseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-case record updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook subscriptions",
        "description": "Paginated list of outbound webhook subscriptions for the project. Signing secrets are never returned.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "url",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact-URL filter."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated subscriptions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "webhooks": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookSubscription"
                          }
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/WebhookPagination"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook subscription",
        "description": "Creates an outbound webhook. The signing `secret` is returned in cleartext exactly once, in this response. Store it immediately: later reads never include it. Requires an owner or admin role.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. `secret` is returned once alongside the subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "webhook": {
                          "$ref": "#/components/schemas/WebhookSubscription"
                        },
                        "secret": {
                          "type": "string",
                          "example": "whsec_3f9a"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, or the destination URL is not accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Per-project webhook limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/webhooks/{webhookId}": {
      "get": {
        "operationId": "getWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Get a webhook subscription",
        "description": "Retrieve a single subscription. The signing secret is not included.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "webhook": {
                          "$ref": "#/components/schemas/WebhookSubscription"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a webhook subscription",
        "description": "Partial update of a subscription. Setting `active` to true clears the failure count. Requires an owner or admin role.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated subscription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "webhook": {
                          "$ref": "#/components/schemas/WebhookSubscription"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook subscription",
        "description": "Permanently deletes the subscription. Requires an owner or admin role.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "deleted": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/{projectId}/webhooks/{webhookId}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "tags": [
          "Webhooks"
        ],
        "summary": "List delivery attempts for a webhook",
        "description": "Paginated delivery history for a subscription, newest first. The request payload is excluded.",
        "parameters": [
          {
            "$ref": "#/components/parameters/projectId"
          },
          {
            "name": "webhookId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "success",
                "failed"
              ]
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "endDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated delivery history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "deliveries": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookDelivery"
                          }
                        },
                        "pagination": {
                          "$ref": "#/components/schemas/WebhookPagination"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  }
}
