{
  "openapi": "3.0.3",
  "info": {
    "title": "HowardFarran.com API",
    "version": "1.0.0",
    "description": "Public, read-only endpoints for discovering and checking the HowardFarran.com API. These endpoints require no credentials and do not read or modify scheduling data."
  },
  "servers": [
    {
      "url": "https://api.howardfarran.com"
    }
  ],
  "security": [],
  "externalDocs": {
    "description": "Public API guide",
    "url": "https://api.howardfarran.com/info"
  },
  "paths": {
    "/": {
      "get": {
        "operationId": "getApiSpecification",
        "summary": "Read the OpenAPI specification",
        "description": "Returns this OpenAPI 3.0 document directly. JSON is the default; request application/yaml with Accept for YAML. Quality values and specificity are respected, with JSON winning equal preferences. Responses are not cached.",
        "responses": {
          "200": {
            "description": "The public API specification.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              },
              "Vary": {
                "$ref": "#/components/headers/AcceptVariation"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenApiDocument"
                }
              },
              "application/yaml": {
                "schema": {
                  "type": "string",
                  "description": "YAML representation of the same OpenAPI document."
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "406": {
            "$ref": "#/components/responses/NotAcceptable"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "default": {
            "$ref": "#/components/responses/GatewayError"
          }
        }
      },
      "head": {
        "operationId": "headApiSpecification",
        "summary": "Check specification response headers",
        "description": "Uses the same Accept negotiation as GET, with no response body.",
        "responses": {
          "200": {
            "description": "Specification headers, with no body.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              },
              "Vary": {
                "$ref": "#/components/headers/AcceptVariation"
              }
            }
          },
          "406": {
            "description": "Neither JSON nor YAML is acceptable. No body."
          },
          "default": {
            "description": "Request failed. HEAD responses have no body."
          }
        }
      }
    },
    "/test": {
      "get": {
        "operationId": "getApiStatus",
        "summary": "Check API liveness",
        "description": "Confirms the API handler is responding. This does not check database or scheduling readiness. Accepts one optional trailing slash. Query parameters have no effect and are not echoed. No credentials are required.",
        "responses": {
          "200": {
            "description": "The API is responding.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiStatus"
                },
                "example": {
                  "status": "ok",
                  "apiVersion": "1",
                  "timestamp": "2026-11-04T18:00:00.000Z"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "default": {
            "$ref": "#/components/responses/GatewayError"
          }
        }
      },
      "head": {
        "operationId": "headApiStatus",
        "summary": "Check liveness response headers",
        "responses": {
          "200": {
            "description": "Liveness headers, with no body.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            }
          },
          "default": {
            "description": "Request failed. HEAD responses have no body."
          }
        }
      }
    },
    "/info": {
      "get": {
        "operationId": "getApiGuide",
        "summary": "Read the public API guide",
        "description": "Accessible HTML documentation. Accepts one optional trailing slash.",
        "responses": {
          "200": {
            "description": "Public documentation with examples; no JavaScript required.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "default": {
            "$ref": "#/components/responses/GatewayError"
          }
        }
      },
      "head": {
        "operationId": "headApiGuide",
        "summary": "Check public guide response headers",
        "responses": {
          "200": {
            "description": "Public guide headers, with no body.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/NoStore"
              }
            }
          },
          "default": {
            "description": "Request failed. HEAD responses have no body."
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "NoStore": {
        "description": "Responses must not be stored in caches.",
        "schema": {
          "type": "string",
          "enum": [
            "no-store"
          ]
        }
      },
      "AcceptVariation": {
        "description": "The representation depends on the Accept request header.",
        "schema": {
          "type": "string",
          "enum": [
            "Accept"
          ]
        }
      }
    },
    "schemas": {
      "ApiStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status",
          "apiVersion",
          "timestamp"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ],
            "description": "The handler successfully answered this request."
          },
          "apiVersion": {
            "type": "string",
            "enum": [
              "1"
            ],
            "description": "Major version of the public API contract."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Response time in UTC, formatted as an ISO 8601 timestamp."
          }
        }
      },
      "OpenApiDocument": {
        "type": "object",
        "required": [
          "openapi",
          "info",
          "servers",
          "paths"
        ],
        "properties": {
          "openapi": {
            "type": "string",
            "enum": [
              "3.0.3"
            ]
          },
          "info": {
            "type": "object"
          },
          "servers": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "paths": {
            "type": "object"
          }
        }
      },
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "not_found",
                  "method_not_allowed",
                  "not_acceptable",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "A public-safe description of the failure."
              }
            }
          }
        }
      }
    },
    "responses": {
      "NotFound": {
        "description": "Unknown endpoint (404); no request values are reflected.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "not_found",
                "message": "Endpoint not found."
              }
            }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "The endpoint does not support this method.",
        "headers": {
          "Allow": {
            "schema": {
              "type": "string",
              "enum": [
                "GET, HEAD, OPTIONS"
              ]
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotAcceptable": {
        "description": "The requested specification format is unavailable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "An unexpected error occurred. Try again later.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "GatewayError": {
        "description": "Requests can also receive 429 (too many requests) or a temporary 5xx response. These responses may use a different body format. Wait before retrying and avoid repeated rapid requests."
      }
    }
  }
}
