{
  "openapi": "3.1.0",
  "info": {
    "title": "Is It Aerated? Community Conditions API",
    "version": "1.0.0",
    "description": "Read current community golf-green conditions and submit user-authorized reports or confirmations. Never infer or invent a condition."
  },
  "servers": [
    { "url": "https://isitaerated.com" }
  ],
  "paths": {
    "/api/courses.json": {
      "get": {
        "operationId": "listCourseConditions",
        "summary": "List monitored courses and their latest shared condition",
        "responses": {
          "200": {
            "description": "Live course condition feed",
            "content": { "application/json": { "schema": { "type": "object" } } }
          }
        }
      }
    },
    "/api/reports": {
      "get": {
        "operationId": "listLatestReports",
        "summary": "List the latest persisted report for each course",
        "responses": { "200": { "description": "Latest reports" } }
      },
      "post": {
        "operationId": "createCourseReport",
        "summary": "Save a structured condition report explicitly supplied by a user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/StructuredReport" }
            }
          }
        },
        "responses": {
          "201": { "description": "Report saved" },
          "422": { "description": "Unknown course or invalid status" },
          "429": { "description": "Rate limit reached" }
        }
      }
    },
    "/api/agent/report": {
      "get": {
        "operationId": "describeAgentReportAction",
        "summary": "Describe the voice and AI report action",
        "responses": { "200": { "description": "Action metadata and examples" } }
      },
      "post": {
        "operationId": "reportConditionFromConversation",
        "summary": "Save a user-authorized report by course name or natural-language utterance",
        "description": "Call only after a user explicitly states the observed course condition. The response includes spokenConfirmation for Siri or another voice agent.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  { "$ref": "#/components/schemas/ConversationalReport" },
                  { "$ref": "#/components/schemas/NamedReport" }
                ]
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Report saved with a voice-ready confirmation" },
          "409": { "description": "Course name is ambiguous" },
          "422": { "description": "Course or status was not understood" },
          "429": { "description": "Rate limit reached" }
        }
      }
    },
    "/api/confirmations": {
      "post": {
        "operationId": "confirmLatestCourseReport",
        "summary": "Confirm that a course's latest report matches the user's observation",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["courseId"],
                "properties": { "courseId": { "type": "string" } },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Confirmation saved or already recorded" },
          "404": { "description": "There is no report to confirm" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Status": {
        "type": "string",
        "enum": ["clear", "recovering", "aerated", "scheduled"]
      },
      "StructuredReport": {
        "type": "object",
        "required": ["courseId", "status"],
        "properties": {
          "courseId": { "type": "string", "examples": ["stoneybrook-golf-club"] },
          "status": { "$ref": "#/components/schemas/Status" },
          "notes": { "type": "string", "maxLength": 500 },
          "reportedBy": { "type": "string", "maxLength": 60 },
          "source": { "type": "string", "enum": ["website", "agent", "siri", "api"] }
        },
        "additionalProperties": false
      },
      "ConversationalReport": {
        "type": "object",
        "required": ["utterance"],
        "properties": {
          "utterance": {
            "type": "string",
            "maxLength": 500,
            "examples": ["Tell Is It Aerated that Stoney Brook is currently aerated"]
          },
          "notes": { "type": "string", "maxLength": 500 },
          "reportedBy": { "type": "string", "maxLength": 60 },
          "source": { "type": "string", "enum": ["agent", "siri"] }
        },
        "additionalProperties": false
      },
      "NamedReport": {
        "type": "object",
        "required": ["course", "status"],
        "properties": {
          "course": { "type": "string", "examples": ["San Carlos Golf Club"] },
          "status": { "$ref": "#/components/schemas/Status" },
          "notes": { "type": "string", "maxLength": 500 },
          "reportedBy": { "type": "string", "maxLength": 60 },
          "source": { "type": "string", "enum": ["agent", "siri"] }
        },
        "additionalProperties": false
      }
    }
  }
}
