{
  "openapi": "3.1.0",
  "info": {
    "title": "Qualitum Public API",
    "version": "1.0.0",
    "summary": "Public, unauthenticated endpoints of the Qualitum agentic validation platform.",
    "description": "Qualitum is an agentic computer system validation (CSV/CQV) platform for life sciences. This specification covers the endpoints Qualitum publishes on the public internet: a service health probe and the GAMP 5 category check, the same heuristic first-pass classifier exposed on qualitum.ai. Customer validation lifecycle APIs (requirements, protocols, executions, signatures, audit trail) run inside each customer's private single-tenant deployment and are documented per tenant; contact api@qualitum.ai for the tenant API specification. All responses are JSON, including errors. No authentication is required for the endpoints below; requests are rate limited by IP.",
    "termsOfService": "https://qualitum.ai/privacy",
    "contact": {
      "name": "Qualitum API support",
      "email": "support@qualitum.ai",
      "url": "https://qualitum.ai/docs/api"
    },
    "license": { "name": "Proprietary", "url": "https://qualitum.ai/privacy" }
  },
  "servers": [
    { "url": "https://qualitum.ai/api/v1", "description": "Public production API" }
  ],
  "externalDocs": {
    "description": "Qualitum API & agent integration documentation",
    "url": "https://qualitum.ai/docs/api"
  },
  "tags": [
    { "name": "Service", "description": "Availability and metadata of the public API." },
    { "name": "Validation", "description": "GxP validation utilities that need no customer data or tenant." }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "getPublicApiHealth",
        "summary": "Check public API availability",
        "description": "Returns the health of the Qualitum public API, the API version and the current server time. Use this to confirm reachability before calling other operations. Never returns customer data.",
        "tags": ["Service"],
        "security": [],
        "responses": {
          "200": {
            "description": "The API is reachable and serving requests.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HealthStatus" },
                "example": { "status": "ok", "service": "qualitum-public-api", "version": "1.0.0", "time": "2026-08-22T09:30:00.000Z", "documentation": "https://qualitum.ai/docs/api" }
              }
            }
          },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" }
        }
      }
    },
    "/gamp-category-check": {
      "post": {
        "operationId": "classifyGampCategory",
        "summary": "Classify a computerised system into a GAMP 5 software category",
        "description": "Takes a plain-language description of a computerised system used in a GxP process and returns a proposed GAMP 5 software category (3 = non-configured product, 4 = configured product, 5 = custom/bespoke software), a confidence level and the reasoning behind the proposal. This is a deterministic keyword heuristic intended as a first pass for triage and scoping conversations: it is indicative only and is not a validation deliverable. Inside a Qualitum deployment the same step runs against the full URS and SOP library and every classification is reviewer-confirmed before it enters the audit trail. No input is stored.",
        "tags": ["Validation"],
        "security": [],
        "requestBody": {
          "required": true,
          "description": "The system description to classify.",
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/GampCategoryCheckRequest" },
              "examples": {
                "configuredLims": {
                  "summary": "Configured LIMS",
                  "value": { "description": "A LIMS configured with our sample workflows, user roles and business rules. No custom code." }
                },
                "customModule": {
                  "summary": "Bespoke calculation module",
                  "value": { "description": "A bespoke calculation module written in-house for our QC lab, with custom code maintained by our IT team." }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A proposed GAMP 5 category with reasoning.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GampCategoryCheckResult" },
                "example": {
                  "category": 4,
                  "categoryLabel": "Configured product",
                  "confidence": "high",
                  "ambiguous": false,
                  "reasoning": "Signals of configuration - workflows, business rules, roles, parameters - place this in Category 4.",
                  "matchedSignals": { "category3": 0, "category4": 4, "category5": 0 },
                  "standard": "GAMP 5 (2nd Edition)",
                  "disclaimer": "Indicative only - the production agent classifies against your full URS and SOP library, and every classification is reviewer-confirmed before it enters the audit trail."
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "413": { "$ref": "#/components/responses/PayloadTooLarge" },
          "415": { "$ref": "#/components/responses/UnsupportedMediaType" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "HealthStatus": {
        "type": "object",
        "description": "Availability of the Qualitum public API.",
        "required": ["status", "service", "version", "time"],
        "properties": {
          "status": { "type": "string", "enum": ["ok", "degraded"], "description": "Overall service state." },
          "service": { "type": "string", "description": "Service identifier.", "const": "qualitum-public-api" },
          "version": { "type": "string", "description": "Semantic version of the public API contract." },
          "time": { "type": "string", "format": "date-time", "description": "Server time in ISO 8601 (UTC)." },
          "documentation": { "type": "string", "format": "uri", "description": "Human-readable API documentation." }
        },
        "additionalProperties": false
      },
      "GampCategoryCheckRequest": {
        "type": "object",
        "description": "A plain-language description of the computerised system to classify.",
        "required": ["description"],
        "properties": {
          "description": {
            "type": "string",
            "minLength": 12,
            "maxLength": 8000,
            "description": "What the system is and how it is used: vendor product or in-house build, whether it is used as supplied, configured, or custom-developed, and its role in the GxP process."
          }
        },
        "additionalProperties": false
      },
      "GampCategoryCheckResult": {
        "type": "object",
        "description": "Proposed GAMP 5 software category and the reasoning behind it.",
        "required": ["category", "categoryLabel", "confidence", "reasoning", "standard", "disclaimer"],
        "properties": {
          "category": { "type": "integer", "enum": [3, 4, 5], "description": "Proposed GAMP 5 software category. 3 = non-configured product, 4 = configured product, 5 = custom/bespoke software." },
          "categoryLabel": { "type": "string", "description": "Human-readable label for the proposed category." },
          "confidence": { "type": "string", "enum": ["low", "moderate", "high"], "description": "Confidence derived from how many category signals the description matched." },
          "ambiguous": { "type": "boolean", "description": "True when no category signal was found and the result fell back to the Category 4 default." },
          "reasoning": { "type": "string", "description": "Plain-text explanation of the classification and its validation consequences." },
          "matchedSignals": {
            "type": "object",
            "description": "Count of keyword signals matched per category.",
            "properties": {
              "category3": { "type": "integer", "minimum": 0, "description": "Non-configured-product signals matched." },
              "category4": { "type": "integer", "minimum": 0, "description": "Configuration signals matched." },
              "category5": { "type": "integer", "minimum": 0, "description": "Custom-development signals matched." }
            },
            "additionalProperties": false
          },
          "standard": { "type": "string", "description": "Standard the categorisation is drawn from." },
          "disclaimer": { "type": "string", "description": "Scope limit of this result." }
        },
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "description": "Structured error response. Every non-2xx response from this API uses this shape.",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "description": "Stable, machine-readable error code.", "examples": ["invalid_request", "not_found", "method_not_allowed", "payload_too_large", "unsupported_media_type", "internal_error"] },
              "message": { "type": "string", "description": "Human-readable description of what went wrong." },
              "field": { "type": "string", "description": "Request field that caused the error, when applicable." },
              "resolution": { "type": "string", "description": "What the caller should do next to succeed." },
              "documentation": { "type": "string", "format": "uri", "description": "Documentation covering this operation." }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request body was missing, malformed, or failed validation.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": { "code": "invalid_request", "message": "Field 'description' is required and must be a string of at least 12 characters.", "field": "description", "resolution": "Send {\"description\": \"...\"} as a JSON object body describing the system.", "documentation": "https://qualitum.ai/docs/api" } }
          }
        }
      },
      "NotFound": {
        "description": "No such API endpoint.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": { "code": "not_found", "message": "No API endpoint at /api/v1/does-not-exist.", "resolution": "Fetch https://qualitum.ai/openapi.json for the list of available operations.", "documentation": "https://qualitum.ai/docs/api" } }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "The HTTP method is not supported by this endpoint.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": { "code": "method_not_allowed", "message": "GET is not supported on /api/v1/gamp-category-check.", "resolution": "Use POST with a JSON body.", "documentation": "https://qualitum.ai/docs/api" } }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The request body exceeded the maximum size.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": { "code": "payload_too_large", "message": "Field 'description' exceeds the 8000 character limit.", "field": "description", "resolution": "Send a shorter system description, or book a working session for a full URS review.", "documentation": "https://qualitum.ai/docs/api" } }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "The request Content-Type is not application/json.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": { "code": "unsupported_media_type", "message": "Content-Type must be application/json.", "resolution": "Set the header 'Content-Type: application/json'.", "documentation": "https://qualitum.ai/docs/api" } }
          }
        }
      }
    },
    "securitySchemes": {}
  },
  "security": []
}
