{
  "openapi": "3.1.0",
  "info": {
    "title": "One Cue Golf supplied-measurement API",
    "version": "1.0.0",
    "description": "Controlled integration for authorized synthetic testing. Consumer coaching is not available. One integration key; 30 admitted authenticated POST attempts per fixed minute and 500 per UTC day across the service. Invalid input after admission counts. Each valid request attempts one experimental Jev classification when configured. No lesson lookup, prose-writing model or measurement persistence."
  },
  "servers": [
    {
      "url": "https://api.onecuegolf.com",
      "description": "Controlled integration endpoint. Requires a separately issued credential."
    }
  ],
  "security": [
    {
      "integrationKey": []
    }
  ],
  "paths": {
    "/api/v1/analyze": {
      "post": {
        "operationId": "analyzeSuppliedMeasurements",
        "summary": "Analyze intentionally supplied right-handed shot measurements",
        "description": "Returns evidence, missing measurements and one next action. Body limit 65536 bytes. No query parameters. Processing order: route, configuration, method, authentication, quota, query, media/encoding, size/body timeout, JSON, measurements. Empty or partial measurements do not prove passed fundamentals. Use a descriptive User-Agent. Cloudflare may return non-JSON errors or terminate incomplete uploads without an HTTP response. The handler body-read deadline is ten seconds after admission, not an end-to-end transport guarantee.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalysisRequest"
              },
              "example": {
                "handedness": "right",
                "intendedShape": "straight",
                "shots": [
                  {
                    "club": "7 iron",
                    "faceAngle": 4,
                    "clubPath": 0
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authoritative deterministic analysis plus a separate experimental Jev classification or an explicit unavailable state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisResponse"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query, JSON or measurements.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing integration key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "404": {
            "description": "Unknown endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "405": {
            "description": "Only POST is supported. Allow: POST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string",
                  "const": "POST"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "408": {
            "description": "Body did not arrive within ten seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "413": {
            "description": "Declared or streamed body exceeds 65536 bytes, or invalid Content-Length.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "415": {
            "description": "Requires application/json and identity content encoding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "429": {
            "description": "Integration allowance exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Positive seconds until the latest exhausted window resets.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected analysis failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          },
          "503": {
            "description": "Missing configuration or quota unavailable. Quota calls have a two-second deadline.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "integrationKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Dedicated server-to-server integration credential. Never use an instructor credential or put a key in a URL."
      }
    },
    "schemas": {
      "Shot": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "club": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40,
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9 _-]{0,39}$",
            "description": "One club per analysis. Leading/trailing comparison uses trimmed identifiers."
          },
          "faceAngle": {
            "type": [
              "number",
              "null"
            ],
            "minimum": -90,
            "maximum": 90,
            "description": "Degrees relative to target; positive right, negative left. Omitted or null means unmeasured, never zero."
          },
          "clubPath": {
            "type": [
              "number",
              "null"
            ],
            "minimum": -90,
            "maximum": 90,
            "description": "Degrees relative to target; positive right, negative left. Omitted or null means unmeasured, never zero."
          },
          "faceToPath": {
            "type": [
              "number",
              "null"
            ],
            "minimum": -180,
            "maximum": 180,
            "description": "Degrees: faceAngle minus clubPath. Positive is open to path. Derived if omitted and both source values exist. Contradictions beyond 0.5 degrees are rejected. Omitted or null means unmeasured, never zero."
          },
          "swingDirection": {
            "type": [
              "number",
              "null"
            ],
            "minimum": -90,
            "maximum": 90,
            "description": "Degrees relative to target; positive right, negative left. Omitted or null means unmeasured, never zero."
          },
          "attackAngle": {
            "type": [
              "number",
              "null"
            ],
            "minimum": -90,
            "maximum": 90,
            "description": "Degrees; downward is negative, upward positive. Omitted or null means unmeasured, never zero."
          },
          "lowPointInches": {
            "type": [
              "number",
              "null"
            ],
            "minimum": -100,
            "maximum": 100,
            "description": "Inches; positive after the ball toward target, negative before the ball. Omitted or null means unmeasured, never zero."
          },
          "smashFactor": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 2,
            "description": "Unitless ball-speed / club-speed ratio. Does not establish strike location. Omitted or null means unmeasured, never zero."
          }
        },
        "required": [
          "club"
        ]
      },
      "AnalysisRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "handedness": {
            "const": "right",
            "type": "string"
          },
          "intendedShape": {
            "type": "string",
            "enum": [
              "straight",
              "draw",
              "fade",
              "unspecified"
            ]
          },
          "shots": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/Shot"
            }
          }
        },
        "required": [
          "handedness",
          "intendedShape",
          "shots"
        ],
        "description": "Analyze only measurements intentionally supplied by the golfer. All shots must have the same trimmed club identifier. The server enforces cross-field face-to-path consistency within 0.5 degrees."
      },
      "MetricEvidence": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "unit": {
            "type": "string",
            "enum": [
              "degrees",
              "inches",
              "ratio"
            ]
          },
          "suppliedCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 200
          },
          "derivedCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 200
          },
          "missingCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 200
          },
          "min": {
            "type": [
              "number",
              "null"
            ]
          },
          "max": {
            "type": [
              "number",
              "null"
            ]
          },
          "mean": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "unit",
          "suppliedCount",
          "derivedCount",
          "missingCount",
          "min",
          "max",
          "mean"
        ]
      },
      "DeterministicAnalysis": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "version": {
            "const": "onecue-measurements-v1",
            "type": "string"
          },
          "sample": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "single-shot",
                  "shot-group"
                ]
              },
              "count": {
                "type": "integer",
                "minimum": 1,
                "maximum": 200
              },
              "club": {
                "type": "string"
              }
            },
            "required": [
              "kind",
              "count",
              "club"
            ]
          },
          "diagnosis": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "measurement-read",
                  "mixed-evidence",
                  "insufficient-evidence"
                ]
              },
              "bucket": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "intentRelation": {
                "type": "string",
                "enum": [
                  "compatible",
                  "different",
                  "not-established"
                ]
              }
            },
            "required": [
              "kind",
              "bucket",
              "description",
              "intentRelation"
            ]
          },
          "evidence": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "faceAngle": {
                "$ref": "#/components/schemas/MetricEvidence"
              },
              "clubPath": {
                "$ref": "#/components/schemas/MetricEvidence"
              },
              "faceToPath": {
                "$ref": "#/components/schemas/MetricEvidence"
              },
              "swingDirection": {
                "$ref": "#/components/schemas/MetricEvidence"
              },
              "attackAngle": {
                "$ref": "#/components/schemas/MetricEvidence"
              },
              "lowPointInches": {
                "$ref": "#/components/schemas/MetricEvidence"
              },
              "smashFactor": {
                "$ref": "#/components/schemas/MetricEvidence"
              }
            },
            "required": [
              "faceAngle",
              "clubPath",
              "faceToPath",
              "swingDirection",
              "attackAngle",
              "lowPointInches",
              "smashFactor"
            ]
          },
          "missingMeasurements": {
            "type": "array",
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "faceAngle",
                "clubPath",
                "faceToPath",
                "swingDirection",
                "attackAngle",
                "lowPointInches",
                "smashFactor"
              ]
            }
          },
          "checks": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "fundamentals": {
                "type": "string",
                "const": "unobserved"
              },
              "lowPoint": {
                "type": "string",
                "enum": [
                  "flagged",
                  "not-flagged",
                  "unknown"
                ]
              },
              "contact": {
                "type": "string",
                "enum": [
                  "flagged",
                  "not-flagged",
                  "unknown"
                ]
              }
            },
            "required": [
              "fundamentals",
              "lowPoint",
              "contact"
            ]
          },
          "nextAction": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "kind": {
                    "const": "collect-measurement",
                    "type": "string"
                  },
                  "measurement": {
                    "type": "string",
                    "enum": [
                      "faceAngle",
                      "clubPath",
                      "faceToPath",
                      "swingDirection",
                      "attackAngle",
                      "lowPointInches",
                      "smashFactor"
                    ]
                  },
                  "text": {
                    "type": "string"
                  }
                },
                "required": [
                  "kind",
                  "measurement",
                  "text"
                ]
              },
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "kind": {
                    "const": "coach-check",
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  }
                },
                "required": [
                  "kind",
                  "text"
                ]
              }
            ]
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "version",
          "sample",
          "diagnosis",
          "evidence",
          "missingMeasurements",
          "checks",
          "nextAction",
          "limitations"
        ]
      },
      "AnalysisResponse": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "version": {
            "const": "onecue-api-v1",
            "type": "string"
          },
          "deterministic": {
            "$ref": "#/components/schemas/DeterministicAnalysis"
          },
          "ai": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "status": { "const": "unavailable", "type": "string" },
                  "reason": { "type": "string", "enum": ["not_configured", "provider_error"] }
                },
                "required": ["status", "reason"]
              },
              {
                "allOf": [
                  { "$ref": "#/components/schemas/AiEvaluation" },
                  {
                    "type": "object",
                    "properties": { "status": { "type": "string", "enum": ["ready", "insufficient_evidence"] } },
                    "required": ["status"]
                  }
                ]
              },
              {
                "allOf": [
                  { "$ref": "#/components/schemas/AiEvaluation" },
                  {
                    "type": "object",
                    "properties": {
                      "status": { "const": "review_required", "type": "string" },
                      "reason": { "const": "different_priority", "type": "string" }
                    },
                    "required": ["status", "reason"]
                  }
                ]
              }
            ]
          }
        },
        "required": [
          "version",
          "deterministic",
          "ai"
        ]
      },
      "AiEvaluation": {
        "type": "object",
        "properties": {
          "judgment": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "model": { "const": "jev-1.13.0", "type": "string" },
              "choice": { "type": "string" },
              "probabilities": { "type": "object", "additionalProperties": { "type": "number", "minimum": 0, "maximum": 1 } },
              "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
            },
            "required": ["model", "choice", "probabilities", "confidence"]
          },
          "usage": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "input_tokens": { "type": "integer", "minimum": 0, "maximum": 65536 },
              "output_tokens": { "type": "integer", "minimum": 0, "maximum": 65536 }
            },
            "required": ["input_tokens", "output_tokens"]
          },
          "durationMs": { "type": "integer", "minimum": 0 }
        },
        "required": ["judgment", "usage", "durationMs"]
      },
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unavailable",
                  "unauthorized",
                  "not_found",
                  "method_not_allowed",
                  "invalid_query",
                  "unsupported_media_type",
                  "payload_too_large",
                  "request_timeout",
                  "invalid_json",
                  "invalid_measurements",
                  "rate_limited",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      }
    }
  }
}
