{
  "openapi": "3.1.0",
  "info": {
    "title": "CourseProfiler Public API",
    "version": "0.1.0",
    "summary": "Composable public API for CRSProf, USRProf, estimation, and race-plan PDF workflows.",
    "description": "Public composable API for CRSProf/USRProf race-plan workflows.\nEndpoints are artifact/job based so external clients and AI agents can build race-plan workflows without browser automation.\nAI agents should ask the user for explicit permission before making POST, DELETE, or other state-changing requests. Read-only GET requests may be used for discovery and status checks.\n"
  },
  "servers": [
    {
      "url": "https://courseprofiler.com",
      "description": "Production origin"
    },
    {
      "url": "http://localhost:8080",
      "description": "Local backend"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "BearerAuth": []
    },
    {}
  ],
  "tags": [
    {
      "name": "Jobs"
    },
    {
      "name": "Artifacts"
    },
    {
      "name": "Uploads"
    },
    {
      "name": "RacePlans"
    },
    {
      "name": "CRSProf"
    },
    {
      "name": "USRProf"
    },
    {
      "name": "Estimates"
    },
    {
      "name": "Exports"
    },
    {
      "name": "Metadata"
    }
  ],
  "paths": {
    "/api/jobs/{job_id}": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "summary": "Get async job status",
        "operationId": "getJob",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "$ref": "#/components/parameters/JobId"
          }
        ],
        "responses": {
          "200": {
            "description": "Job status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/artifacts/{artifact_id}": {
      "get": {
        "tags": [
          "Artifacts"
        ],
        "summary": "Get artifact metadata and a private pre-signed download URL",
        "operationId": "getArtifact",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "$ref": "#/components/parameters/ArtifactId"
          }
        ],
        "responses": {
          "200": {
            "description": "Artifact metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Artifact"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "Artifact expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Artifacts"
        ],
        "summary": "Delete an artifact before its normal expiry",
        "operationId": "deleteArtifact",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/ArtifactId"
          }
        ],
        "responses": {
          "204": {
            "description": "Artifact deleted or already unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/artifact-uploads": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "summary": "Create a direct upload session for a local file",
        "description": "Upload bridge for clients and AI assistants with local/sandbox files. Creates a short-lived pre-signed upload target. Complete the session to register an opaque source_file artifact ID.\n",
        "operationId": "createArtifactUpload",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArtifactUploadCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload session created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtifactUploadSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/artifact-uploads/{upload_id}": {
      "get": {
        "tags": [
          "Uploads"
        ],
        "summary": "Get upload session status",
        "operationId": "getArtifactUpload",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "$ref": "#/components/parameters/UploadId"
          }
        ],
        "responses": {
          "200": {
            "description": "Upload session status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtifactUploadSession"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "Upload session expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Uploads"
        ],
        "summary": "Cancel an incomplete upload session",
        "operationId": "cancelArtifactUpload",
        "x-courseprofiler-status": "proposed",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/UploadId"
          }
        ],
        "responses": {
          "204": {
            "description": "Upload session cancelled or already unavailable"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/artifact-uploads/{upload_id}/complete": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "summary": "Complete an upload session and register a source_file artifact",
        "operationId": "completeArtifactUpload",
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/UploadId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArtifactUploadCompleteRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Uploaded file registered as an artifact",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Artifact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "Upload session expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/race-plans": {
      "post": {
        "tags": [
          "RacePlans"
        ],
        "summary": "Orchestrate course import, segmentation, runner import, estimation, and optional PDF export",
        "description": "Optional convenience workflow for AI agents and simpler API clients. Creates one top-level async job that composes the CRSProf import, segment generation, USRProf import, estimate, and race-plan PDF export capabilities. Atomic endpoints remain the canonical API surface.\n\nPersonalized race plans require runner input. CourseProfiler does not create meaningful pacing estimates from a course alone. Before calling this endpoint, make sure the user provides one runner input: a USRProf artifact ID, a public HTTPS URL to a USRProf file, local GPX/FIT/CRSProf evidence files uploaded through /api/artifact-uploads then imported with /api/usrprof/imports, public HTTPS URLs to runner evidence files, or a USRProf profile created through CourseProfiler's Strava import feature. Strava import requires browser-based user authentication and consent; direct the user to authenticate in the CourseProfiler web app, then use the resulting USRProf artifact/profile when available. If no runner profile or evidence is available, import/analyze the course only and ask the user for runner evidence before creating a race plan.\n",
        "operationId": "createRacePlan",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RacePlanCreateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/crsprof/imports": {
      "post": {
        "tags": [
          "CRSProf"
        ],
        "summary": "Import or create a CRSProf artifact",
        "description": "Async operation. Creates a CRSProf artifact from GPX, FIT, CRSProf, public HTTPS URL, existing import/share ID, or raw CRSProf JSON.\n",
        "operationId": "createCrsprofImport",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrsprofImportRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/crsprof/segments": {
      "post": {
        "tags": [
          "CRSProf"
        ],
        "summary": "Generate or update CRSProf segments",
        "description": "Async when generation or optimization is requested. Produces an updated CRSProf artifact.",
        "operationId": "createCrsprofSegments",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrsprofSegmentsRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/crsprof/waypoints": {
      "post": {
        "tags": [
          "CRSProf"
        ],
        "summary": "Enrich a CRSProf artifact with waypoint/resource data",
        "description": "Async operation. Applies structured waypoint/resource/cutoff data or parsed aid-station table text to an existing CRSProf artifact and returns an enriched CRSProf artifact. Use this before segment generation and race-plan estimation when official aid stations, water/food/crew resources, cutoffs, or split separators are available.\n",
        "operationId": "createCrsprofWaypointEnrichment",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrsprofWaypointsRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/crsprof/validate": {
      "post": {
        "tags": [
          "CRSProf"
        ],
        "summary": "Validate a CRSProf artifact or payload",
        "operationId": "validateCrsprof",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrsprofValidateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/api/usrprof/imports": {
      "post": {
        "tags": [
          "USRProf"
        ],
        "summary": "Import or create a USRProf artifact",
        "operationId": "createUsrprofImport",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UsrprofImportRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/usrprof/validate": {
      "post": {
        "tags": [
          "USRProf"
        ],
        "summary": "Validate a USRProf artifact or payload",
        "operationId": "validateUsrprof",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UsrprofValidateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/api/estimates": {
      "post": {
        "tags": [
          "Estimates"
        ],
        "summary": "Compatibility alias for segment-evidence estimation",
        "description": "Backward-compatible alias for POST /api/estimates/segment-evidence. New clients should use the model-specific endpoint names.",
        "operationId": "createEstimate",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/estimates/segment-evidence": {
      "post": {
        "tags": [
          "Estimates"
        ],
        "summary": "Estimate plan times using the segment-evidence model",
        "description": "Async operation. Uses CRSProf and USRProf artifacts to produce an updated planned CRSProf artifact and optional summary JSON artifact.",
        "operationId": "createSegmentEvidenceEstimate",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/estimates/segment-evidence/selected-segments": {
      "post": {
        "tags": [
          "Estimates"
        ],
        "summary": "Estimate selected target segments using the segment-evidence model",
        "description": "Responsive operation for interactive UI use. Accepts selected segment targets plus either raw USRProf JSON or a USRProf artifact ID, and returns estimates/rejections with diagnostics directly.",
        "operationId": "estimateSegmentEvidenceSelectedSegments",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SelectedSegmentEvidenceEstimateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Selected target estimates and rejected targets",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SelectedSegmentEvidenceEstimateResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/estimates/fatigue-form": {
      "post": {
        "tags": [
          "Estimates"
        ],
        "summary": "Estimate plan times using the fatigue/form model",
        "description": "Async operation. Uses CRSProf options.estimation fatigue/form settings to produce an updated planned CRSProf artifact.",
        "operationId": "createFatigueFormEstimate",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FatigueFormEstimateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/estimates/fatigue-form/selected-segments": {
      "post": {
        "tags": [
          "Estimates"
        ],
        "summary": "Estimate selected target segments using the fatigue/form model",
        "description": "Responsive operation for interactive UI use. Runs the fatigue/form model over the full CRSProf to preserve fatigue progression, then returns a CRSProf-shaped payload with newly estimated plan overlays only on the selected segments.",
        "operationId": "estimateFatigueFormSelectedSegments",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SelectedFatigueFormEstimateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "CRSProf-shaped payload with selected segment plan overlays updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrsprofDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/exports/race-plan-pdf": {
      "post": {
        "tags": [
          "Exports"
        ],
        "summary": "Export a prepared CRSProf artifact as a race-plan PDF",
        "description": "Async operation. Produces a PDF artifact matching the current browser hi-fi PDF.",
        "operationId": "createRacePlanPdfExport",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RacePlanPdfExportRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/AcceptedJob"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/crsprof/schemas": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "List CRSProf schemas",
        "operationId": "listCrsprofSchemas",
        "x-openai-isConsequential": false,
        "responses": {
          "200": {
            "description": "Schema list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/crsprof/schemas/{version}": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "Get CRSProf schema metadata",
        "operationId": "getCrsprofSchemaMetadata",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Schema metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/crsprof/schemas/{version}/json-schema": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "Get CRSProf JSON Schema",
        "operationId": "getCrsprofJsonSchema",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON Schema",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/crsprof/interpretations": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "List CRSProf semantic interpretations",
        "operationId": "listCrsprofInterpretations",
        "x-openai-isConsequential": false,
        "responses": {
          "200": {
            "description": "Interpretation list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/crsprof/interpretations/{profile}": {
      "get": {
        "tags": [
          "Metadata"
        ],
        "summary": "Get CRSProf semantic interpretation",
        "operationId": "getCrsprofInterpretation",
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "profile",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Interpretation metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Optional API key. Invalid keys return 401; absent keys use unauthenticated public quotas."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Optional bearer API key. Invalid tokens return 401; absent tokens use unauthenticated public quotas."
      }
    },
    "parameters": {
      "JobId": {
        "name": "job_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^job_[A-Za-z0-9_-]+$"
        }
      },
      "ArtifactId": {
        "name": "artifact_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^art_[A-Za-z0-9_-]+$"
        }
      },
      "UploadId": {
        "name": "upload_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^upl_[A-Za-z0-9_-]+$"
        }
      }
    },
    "responses": {
      "AcceptedJob": {
        "description": "Async job accepted",
        "headers": {
          "Location": {
            "description": "URL of the job status resource",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Job"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Payload too large",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds before retry"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "Job": {
        "type": "object",
        "required": [
          "job_id",
          "status",
          "status_url",
          "created_at",
          "expires_at",
          "result_artifacts",
          "warnings",
          "errors"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "examples": [
              "job_01JABCDEF"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "status_url": {
            "type": "string",
            "examples": [
              "/api/jobs/job_01JABCDEF"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "result_artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          },
          "progress": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "JobStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "succeeded",
          "failed",
          "expired",
          "cancelled"
        ]
      },
      "Artifact": {
        "type": "object",
        "required": [
          "artifact_id",
          "type",
          "content_type",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "artifact_id": {
            "type": "string",
            "examples": [
              "art_01JABCDEF"
            ]
          },
          "type": {
            "$ref": "#/components/schemas/ArtifactType"
          },
          "content_type": {
            "type": "string",
            "examples": [
              "application/vnd.courseprof+json",
              "application/vnd.usrprof+json",
              "application/pdf"
            ]
          },
          "file_name": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 0
          },
          "download_url": {
            "type": "string",
            "format": "uri",
            "description": "Private pre-signed URL. May be omitted for expired artifacts."
          },
          "download_url_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          }
        }
      },
      "ArtifactType": {
        "type": "string",
        "enum": [
          "source_file",
          "crsprof",
          "usrprof",
          "pdf",
          "json",
          "diagnostics"
        ]
      },
      "ArtifactUploadCreateRequest": {
        "type": "object",
        "required": [
          "file_name",
          "content_type",
          "size_bytes",
          "purpose"
        ],
        "additionalProperties": false,
        "properties": {
          "file_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "examples": [
              "course.gpx"
            ]
          },
          "content_type": {
            "type": "string",
            "examples": [
              "application/gpx+xml",
              "application/octet-stream",
              "application/vnd.courseprof+json"
            ]
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 26214400
          },
          "purpose": {
            "$ref": "#/components/schemas/ArtifactUploadPurpose",
            "description": "Use `course_source` for course files, `runner_evidence` for activity/evidence files, and `usrprof_source` for local `.usrprof` profile files."
          },
          "checksum": {
            "$ref": "#/components/schemas/ArtifactUploadChecksum"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ArtifactUploadCompleteRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "checksum": {
            "$ref": "#/components/schemas/ArtifactUploadChecksum"
          }
        }
      },
      "ArtifactUploadChecksum": {
        "type": "object",
        "required": [
          "algorithm",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "algorithm": {
            "type": "string",
            "enum": [
              "sha256"
            ]
          },
          "value": {
            "type": "string",
            "pattern": "^[a-fA-F0-9]{64}$"
          }
        }
      },
      "ArtifactUploadPurpose": {
        "type": "string",
        "description": "Upload purpose. Use `usrprof_source` when uploading an existing local `.usrprof` runner profile; completed `source_file` artifacts can then be passed through `runner.sources` or `/api/usrprof/imports`.",
        "enum": [
          "course_source",
          "runner_evidence",
          "usrprof_source",
          "generic_source"
        ]
      },
      "ArtifactUploadSession": {
        "type": "object",
        "required": [
          "upload_id",
          "status",
          "expires_at"
        ],
        "properties": {
          "upload_id": {
            "type": "string",
            "examples": [
              "upl_01JABCDEF"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/ArtifactUploadStatus"
          },
          "upload": {
            "$ref": "#/components/schemas/ArtifactUploadTarget"
          },
          "complete_url": {
            "type": "string",
            "examples": [
              "/api/artifact-uploads/upl_01JABCDEF/complete"
            ]
          },
          "artifact_id": {
            "type": "string",
            "description": "Present after completion."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          }
        }
      },
      "ArtifactUploadStatus": {
        "type": "string",
        "enum": [
          "waiting_for_upload",
          "uploaded",
          "completed",
          "failed",
          "expired",
          "cancelled"
        ]
      },
      "ArtifactUploadTarget": {
        "type": "object",
        "required": [
          "method",
          "url",
          "expires_at",
          "max_size_bytes"
        ],
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "PUT"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Private pre-signed direct-upload URL."
          },
          "headers": {
            "type": "object",
            "description": "Headers that must be replayed exactly on the direct PUT upload request. Missing or changed headers can invalidate the pre-signed URL.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "max_size_bytes": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "ApiProblem": {
        "type": "object",
        "required": [
          "code",
          "message",
          "severity"
        ],
        "properties": {
          "code": {
            "type": "string",
            "examples": [
              "missing_elevation",
              "payload_too_large",
              "rate_limited"
            ]
          },
          "message": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "warning",
              "error"
            ]
          },
          "field": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "errors"
        ],
        "properties": {
          "errors": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "InputSource": {
        "type": "object",
        "required": [
          "kind"
        ],
        "discriminator": {
          "propertyName": "kind"
        },
        "oneOf": [
          {
            "$ref": "#/components/schemas/ArtifactInputSource"
          },
          {
            "$ref": "#/components/schemas/UrlInputSource"
          },
          {
            "$ref": "#/components/schemas/RawJsonInputSource"
          },
          {
            "$ref": "#/components/schemas/ImportIdInputSource"
          }
        ]
      },
      "ArtifactInputSource": {
        "type": "object",
        "required": [
          "kind",
          "artifact_id"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "const": "artifact"
          },
          "artifact_id": {
            "type": "string"
          }
        }
      },
      "UrlInputSource": {
        "type": "object",
        "required": [
          "kind",
          "url"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "const": "url"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "RawJsonInputSource": {
        "type": "object",
        "required": [
          "kind",
          "payload"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "const": "raw_json"
          },
          "payload": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ImportIdInputSource": {
        "type": "object",
        "required": [
          "kind",
          "import_id"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "const": "import_id"
          },
          "import_id": {
            "type": "string"
          }
        }
      },
      "CrsprofImportOptions": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "profile",
              "execution",
              "preserve"
            ]
          },
          "include_segments": {
            "type": "boolean"
          },
          "analysis_options": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "CrsprofSegmentOptions": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "algorithm": {
            "type": "string",
            "enum": [
              "GREEDY",
              "SLOPE_BASED",
              "PACE_BASED"
            ]
          },
          "flat_grade": {
            "type": "number",
            "description": "Flat grade threshold in percent."
          },
          "slope_change_threshold": {
            "type": "number",
            "description": "Slope-change threshold in percent."
          },
          "flat_max_delta": {
            "type": "number",
            "description": "Optional maximum elevation delta for flat segments, in meters."
          },
          "minimum_distance": {
            "type": "number",
            "description": "Minimum segment distance in meters."
          },
          "flexible_minimum_distance": {
            "type": "boolean"
          },
          "flexible_flat_max_delta": {
            "type": "boolean"
          },
          "instant_mode": {
            "type": "boolean"
          },
          "include_routes": {
            "type": "boolean"
          }
        }
      },
      "UsrprofImportOptions": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "file_name": {
            "type": "string"
          }
        }
      },
      "EstimateOptions": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "strategy": {
            "type": "string",
            "enum": [
              "segment_evidence"
            ],
            "default": "segment_evidence"
          },
          "gap_algorithm": {
            "type": "string",
            "enum": [
              "POLYNOMIAL",
              "CONSTANT_POWER",
              "MINETTI"
            ]
          },
          "flat_grade": {
            "type": "number",
            "description": "Optional flat-grade threshold passed to segment-evidence locomotion planning."
          }
        }
      },
      "EstimateOutputOptions": {
        "type": "object",
        "properties": {
          "include_summary_artifact": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "RacePlanPdfOptions": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "subtitle": {
            "type": "string"
          },
          "unit_system": {
            "type": "string",
            "enum": [
              "metric",
              "imperial"
            ]
          },
          "file_name": {
            "type": "string"
          }
        }
      },
      "RacePlanCreateRequest": {
        "type": "object",
        "required": [
          "course",
          "runner"
        ],
        "properties": {
          "course": {
            "$ref": "#/components/schemas/RacePlanCourseInput"
          },
          "runner": {
            "$ref": "#/components/schemas/RacePlanRunnerInput"
          },
          "estimate": {
            "$ref": "#/components/schemas/RacePlanEstimateInput"
          },
          "pdf": {
            "$ref": "#/components/schemas/RacePlanPdfInput"
          }
        }
      },
      "RacePlanCourseInput": {
        "type": "object",
        "description": "Exactly one of source or crsprof_artifact_id should be supplied.",
        "oneOf": [
          {
            "required": [
              "source"
            ]
          },
          {
            "required": [
              "crsprof_artifact_id"
            ]
          }
        ],
        "properties": {
          "source": {
            "$ref": "#/components/schemas/InputSource"
          },
          "crsprof_artifact_id": {
            "type": "string"
          },
          "import_options": {
            "$ref": "#/components/schemas/CrsprofImportOptions"
          },
          "waypoints": {
            "description": "Optional waypoint/resource enrichment to apply before segment generation. Use this for official aid stations, water/food/crew resources, cutoffs, and course points.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/StructuredWaypointEnrichment"
              },
              {
                "$ref": "#/components/schemas/TableWaypointEnrichment"
              }
            ]
          },
          "segments": {
            "$ref": "#/components/schemas/RacePlanSegmentsInput"
          }
        }
      },
      "RacePlanSegmentsInput": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "generate",
              "skip"
            ],
            "default": "generate"
          },
          "options": {
            "$ref": "#/components/schemas/CrsprofSegmentOptions"
          }
        }
      },
      "RacePlanRunnerInput": {
        "type": "object",
        "description": "Exactly one of usrprof_artifact_id or sources should be supplied. Use usrprof_artifact_id for an existing USRProf artifact/profile. Use sources for public HTTPS runner evidence/profile URLs or source_file artifacts created via /api/artifact-uploads. Strava-based profiles must be created after browser-based user authentication in the CourseProfiler web app, then supplied as a USRProf artifact/profile.",
        "oneOf": [
          {
            "required": [
              "usrprof_artifact_id"
            ]
          },
          {
            "required": [
              "sources"
            ]
          }
        ],
        "properties": {
          "usrprof_artifact_id": {
            "type": "string"
          },
          "sources": {
            "type": "array",
            "minItems": 1,
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ArtifactInputSource"
                },
                {
                  "$ref": "#/components/schemas/UrlInputSource"
                }
              ]
            }
          },
          "import_options": {
            "$ref": "#/components/schemas/UsrprofImportOptions"
          }
        }
      },
      "RacePlanEstimateInput": {
        "type": "object",
        "properties": {
          "options": {
            "$ref": "#/components/schemas/EstimateOptions"
          },
          "output": {
            "$ref": "#/components/schemas/EstimateOutputOptions"
          }
        }
      },
      "RacePlanPdfInput": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "options": {
            "$ref": "#/components/schemas/RacePlanPdfOptions"
          }
        }
      },
      "CrsprofImportRequest": {
        "type": "object",
        "required": [
          "source"
        ],
        "properties": {
          "source": {
            "$ref": "#/components/schemas/InputSource"
          },
          "options": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "profile",
                  "execution",
                  "preserve"
                ]
              },
              "include_segments": {
                "type": "boolean"
              },
              "analysis_options": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "CrsprofSegmentsRequest": {
        "type": "object",
        "required": [
          "crsprof_artifact_id"
        ],
        "properties": {
          "crsprof_artifact_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "generate"
            ],
            "default": "generate"
          },
          "options": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "algorithm": {
                "type": "string",
                "enum": [
                  "GREEDY",
                  "SLOPE_BASED",
                  "PACE_BASED"
                ]
              },
              "flat_grade": {
                "type": "number",
                "description": "Flat grade threshold in percent."
              },
              "slope_change_threshold": {
                "type": "number",
                "description": "Slope-change threshold in percent."
              },
              "flat_max_delta": {
                "type": "number",
                "description": "Optional maximum elevation delta for flat segments, in meters."
              },
              "minimum_distance": {
                "type": "number",
                "description": "Minimum segment distance in meters."
              },
              "flexible_minimum_distance": {
                "type": "boolean"
              },
              "flexible_flat_max_delta": {
                "type": "boolean"
              },
              "instant_mode": {
                "type": "boolean"
              },
              "include_routes": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "CrsprofWaypointsRequest": {
        "type": "object",
        "required": [
          "course",
          "waypoints"
        ],
        "properties": {
          "course": {
            "type": "object",
            "required": [
              "crsprof_artifact_id"
            ],
            "properties": {
              "crsprof_artifact_id": {
                "type": "string",
                "description": "Existing CRSProf artifact to enrich."
              }
            }
          },
          "waypoints": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/StructuredWaypointEnrichment"
              },
              {
                "$ref": "#/components/schemas/TableWaypointEnrichment"
              }
            ]
          }
        }
      },
      "StructuredWaypointEnrichment": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "structured"
            ],
            "default": "structured"
          },
          "distance_unit": {
            "type": "string",
            "enum": [
              "m",
              "km",
              "mi"
            ],
            "default": "km"
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WaypointItem"
            }
          }
        }
      },
      "TableWaypointEnrichment": {
        "type": "object",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "table"
            ]
          },
          "text": {
            "type": "string",
            "description": "Copied official aid-station/course-point table text. Provide either text or table_url."
          },
          "table_url": {
            "type": "string",
            "format": "uri",
            "description": "Public HTTPS URL for an official aid-station/course-point table, HTML page, or PDF. Provide either text or table_url."
          },
          "source_ref": {
            "type": "string",
            "description": "Optional source URL/page/table reference."
          },
          "start_time": {
            "type": "string",
            "description": "Optional race start time used when parsing clock-based cutoffs."
          }
        }
      },
      "WaypointItem": {
        "type": "object",
        "required": [
          "name",
          "distance"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "distance": {
            "type": "number",
            "minimum": 0
          },
          "distance_unit": {
            "type": "string",
            "enum": [
              "m",
              "km",
              "mi"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "start",
              "finish",
              "aid",
              "checkpoint",
              "water",
              "crew",
              "other"
            ]
          },
          "resources": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "water",
                "water_source",
                "drinks",
                "fruit",
                "food",
                "toilet",
                "medic",
                "chrono",
                "crew",
                "pacer",
                "car",
                "bus",
                "ambulance",
                "shuttle",
                "drop_bag",
                "withdrawal",
                "binoculars",
                "hot_meal",
                "tent",
                "showers",
                "changing_room",
                "physiotherapy",
                "rest_area",
                "start_flag",
                "finish_flag"
              ]
            },
            "uniqueItems": true
          },
          "cutoff": {
            "type": "string",
            "pattern": "^\\\\d{1,3}:\\\\d{2}$",
            "description": "Total elapsed cutoff in HH:MM. HH may be greater than 24."
          },
          "cutoff_kind": {
            "type": "string",
            "enum": [
              "enter",
              "leave"
            ]
          },
          "split_separator": {
            "type": "boolean",
            "description": "Reserved for explicit split separator behavior; current enrichment preserves existing CourseProfiler semantics."
          },
          "notes": {
            "type": "string"
          },
          "source_text": {
            "type": "string"
          },
          "source_ref": {
            "type": "string"
          }
        }
      },
      "CrsprofValidateRequest": {
        "type": "object",
        "oneOf": [
          {
            "required": [
              "crsprof_artifact_id"
            ]
          },
          {
            "required": [
              "crsprof"
            ]
          }
        ],
        "properties": {
          "crsprof_artifact_id": {
            "type": "string"
          },
          "crsprof": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "UsrprofImportRequest": {
        "type": "object",
        "required": [
          "sources"
        ],
        "properties": {
          "sources": {
            "type": "array",
            "minItems": 1,
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ArtifactInputSource"
                },
                {
                  "$ref": "#/components/schemas/UrlInputSource"
                }
              ]
            },
            "description": "First implementation slice supports CRSProf/USRProf artifact IDs and public HTTPS URLs for supported evidence files."
          },
          "options": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "file_name": {
                "type": "string"
              }
            }
          }
        }
      },
      "UsrprofValidateRequest": {
        "type": "object",
        "oneOf": [
          {
            "required": [
              "usrprof_artifact_id"
            ]
          },
          {
            "required": [
              "usrprof"
            ]
          }
        ],
        "properties": {
          "usrprof_artifact_id": {
            "type": "string"
          },
          "usrprof": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "EstimateRequest": {
        "type": "object",
        "required": [
          "crsprof_artifact_id",
          "usrprof_artifact_id"
        ],
        "properties": {
          "crsprof_artifact_id": {
            "type": "string"
          },
          "usrprof_artifact_id": {
            "type": "string"
          },
          "options": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "strategy": {
                "type": "string",
                "enum": [
                  "segment_evidence"
                ],
                "default": "segment_evidence"
              },
              "gap_algorithm": {
                "type": "string",
                "enum": [
                  "POLYNOMIAL",
                  "CONSTANT_POWER",
                  "MINETTI"
                ]
              },
              "flat_grade": {
                "type": "number",
                "description": "Optional flat-grade threshold passed to segment-evidence locomotion planning."
              }
            }
          },
          "output": {
            "type": "object",
            "properties": {
              "include_summary_artifact": {
                "type": "boolean",
                "default": true
              }
            }
          }
        }
      },
      "FatigueFormEstimateRequest": {
        "type": "object",
        "oneOf": [
          {
            "required": [
              "crsprof_artifact_id"
            ]
          },
          {
            "required": [
              "crsprof"
            ]
          }
        ],
        "properties": {
          "crsprof_artifact_id": {
            "type": "string",
            "description": "CRSProf artifact with options.estimation fatigue/form settings."
          },
          "crsprof": {
            "type": "object",
            "additionalProperties": true,
            "description": "Inline CRSProf payload with options.estimation fatigue/form settings."
          },
          "options": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional CRSProf options object to merge before estimation."
          },
          "output": {
            "type": "object",
            "properties": {
              "include_summary_artifact": {
                "type": "boolean",
                "default": false
              }
            }
          }
        }
      },
      "SelectedSegmentEvidenceEstimateRequest": {
        "type": "object",
        "required": [
          "segments"
        ],
        "oneOf": [
          {
            "required": [
              "usrprof_artifact_id"
            ]
          },
          {
            "required": [
              "usrprof"
            ]
          }
        ],
        "properties": {
          "usrprof_artifact_id": {
            "type": "string",
            "description": "USRProf artifact to use as evidence source."
          },
          "usrprof": {
            "type": "object",
            "additionalProperties": true,
            "description": "Inline USRProf payload to use as evidence source."
          },
          "segments": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Ordered selected targets with segment_index, geometry, horizon, optional start_state, and estimate flag."
          },
          "options": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional analysis settings such as gap_algorithm and flat_grade under options.analysis."
          }
        }
      },
      "SelectedFatigueFormEstimateRequest": {
        "type": "object",
        "required": [
          "segments"
        ],
        "oneOf": [
          {
            "required": [
              "crsprof_artifact_id"
            ]
          },
          {
            "required": [
              "crsprof"
            ]
          }
        ],
        "properties": {
          "crsprof_artifact_id": {
            "type": "string",
            "description": "CRSProf artifact with options.estimation fatigue/form settings."
          },
          "crsprof": {
            "type": "object",
            "additionalProperties": true,
            "description": "Inline CRSProf payload with options.estimation fatigue/form settings."
          },
          "segments": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "properties": {
                "segment_index": {
                  "type": "integer",
                  "minimum": 0
                },
                "index": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "additionalProperties": true
            },
            "description": "Selected targets by canonical segment_index or index. The model runs over the full course; only selected segment plan overlays are returned as newly updated."
          },
          "options": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional CRSProf options object to merge before estimation."
          }
        }
      },
      "SelectedSegmentEvidenceEstimateResponse": {
        "type": "object",
        "required": [
          "estimates",
          "rejected"
        ],
        "properties": {
          "estimates": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "rejected": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "RacePlanPdfExportRequest": {
        "type": "object",
        "required": [
          "crsprof_artifact_id"
        ],
        "properties": {
          "crsprof_artifact_id": {
            "type": "string"
          },
          "options": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string"
              },
              "subtitle": {
                "type": "string"
              },
              "unit_system": {
                "type": "string",
                "enum": [
                  "metric",
                  "imperial"
                ]
              },
              "file_name": {
                "type": "string"
              }
            }
          }
        }
      },
      "ValidationResult": {
        "type": "object",
        "required": [
          "valid",
          "warnings",
          "errors"
        ],
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "artifact": {
            "$ref": "#/components/schemas/Artifact"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiProblem"
            }
          }
        }
      }
    }
  },
  "x-courseprofiler-operational-defaults": {
    "artifact_retention": "PT24H",
    "presigned_download_url_ttl": "PT15M",
    "public_url_fetch_timeout": "PT10S",
    "public_url_max_redirects": 4,
    "unauthenticated_concurrent_jobs_per_ip": 2,
    "unauthenticated_queued_jobs_per_ip": 10,
    "unauthenticated_requests_per_minute_per_ip": 60,
    "unauthenticated_requests_per_hour_per_ip": 1000,
    "authenticated_requests_per_minute_per_api_key": 1200,
    "authenticated_requests_per_hour_per_api_key": 10000,
    "upload_size_limit_bytes": 26214400,
    "json_body_size_limit_bytes": 10485760
  }
}
