{
  "openapi": "3.1.0",
  "info": {
    "title": "Fence Calculator API",
    "version": "1.0.0",
    "description": "Programmatic fence material estimation arithmetic. Computes required fence posts, rails, pickets, panels, post holes, and concrete bags from fence dimensions, materials, and gate openings.",
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/licenses/MIT"
    },
    "contact": {
      "name": "Fence Calculator Support",
      "url": "https://fencecalculator.xyz/about"
    }
  },
  "servers": [
    {
      "url": "https://fencecalculator.xyz",
      "description": "Production server"
    }
  ],
  "paths": {
    "/api/calculate": {
      "post": {
        "summary": "Calculate fence materials and post requirements",
        "description": "Computes comprehensive fence component counts including line/corner/end/gate posts, rails, pickets or panels, post hole dimensions, and premix concrete bags.",
        "operationId": "calculateFenceMaterials",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FenceCalculationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful fence calculation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FenceCalculationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input parameter or out-of-range value",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "Calculate fence materials via query parameters",
        "description": "Computes fence components using URL query parameters for fast agent and script lookups without a request body.",
        "operationId": "getFenceCalculation",
        "parameters": [
          {
            "name": "lengthFt",
            "in": "query",
            "required": true,
            "description": "Total fence run length in feet",
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 50000,
              "example": 150
            }
          },
          {
            "name": "heightFt",
            "in": "query",
            "required": false,
            "description": "Fence height in feet (default: 6)",
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 20,
              "default": 6
            }
          },
          {
            "name": "material",
            "in": "query",
            "required": false,
            "description": "Fence material type (default: pt-pine)",
            "schema": {
              "type": "string",
              "enum": [
                "pt-pine",
                "cedar",
                "redwood",
                "vinyl",
                "chain-link",
                "aluminum",
                "composite",
                "split-rail"
              ],
              "default": "pt-pine"
            }
          },
          {
            "name": "style",
            "in": "query",
            "required": false,
            "description": "Fence style (default: privacy)",
            "schema": {
              "type": "string",
              "enum": [
                "privacy",
                "picket",
                "shadowbox",
                "board-on-board",
                "panel",
                "ranch-two-rail",
                "ranch-three-rail",
                "standard",
                "ornamental"
              ],
              "default": "privacy"
            }
          },
          {
            "name": "postSpacingFt",
            "in": "query",
            "required": false,
            "description": "Distance between posts in feet (default: 8)",
            "schema": {
              "type": "number",
              "minimum": 4,
              "maximum": 12,
              "default": 8
            }
          },
          {
            "name": "gateCount",
            "in": "query",
            "required": false,
            "description": "Number of standard walk gates (default: 1)",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 20,
              "default": 1
            }
          },
          {
            "name": "gateWidthFt",
            "in": "query",
            "required": false,
            "description": "Width of each gate in feet (default: 4)",
            "schema": {
              "type": "number",
              "minimum": 3,
              "maximum": 16,
              "default": 4
            }
          },
          {
            "name": "cornerCount",
            "in": "query",
            "required": false,
            "description": "Number of corner posts (default: 4)",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 4
            }
          },
          {
            "name": "endCount",
            "in": "query",
            "required": false,
            "description": "Number of terminal end posts (default: 2)",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 2
            }
          },
          {
            "name": "wasteFactorPercent",
            "in": "query",
            "required": false,
            "description": "Waste allowance percentage for pickets and materials (default: 10)",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 30,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful fence calculation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FenceCalculationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input parameter or out-of-range value",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "summary": "API health check and capability metadata",
        "description": "Returns service status, version, and links to documentation.",
        "operationId": "getApiHealth",
        "responses": {
          "200": {
            "description": "API is healthy and operational",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "FenceCalculationRequest": {
        "type": "object",
        "required": [
          "lengthFt"
        ],
        "properties": {
          "lengthFt": {
            "type": "number",
            "description": "Total fence perimeter/run length in feet",
            "example": 150
          },
          "heightFt": {
            "type": "number",
            "description": "Fence height in feet",
            "default": 6,
            "example": 6
          },
          "material": {
            "type": "string",
            "enum": [
              "pt-pine",
              "cedar",
              "redwood",
              "vinyl",
              "chain-link",
              "aluminum",
              "composite",
              "split-rail"
            ],
            "default": "pt-pine",
            "example": "pt-pine"
          },
          "style": {
            "type": "string",
            "enum": [
              "privacy",
              "picket",
              "shadowbox",
              "board-on-board",
              "panel",
              "ranch-two-rail",
              "ranch-three-rail",
              "standard",
              "ornamental"
            ],
            "default": "privacy",
            "example": "privacy"
          },
          "postSpacingFt": {
            "type": "number",
            "description": "Span between post centres in feet",
            "default": 8,
            "example": 8
          },
          "railCount": {
            "type": "integer",
            "description": "Number of horizontal rails per section (optional, auto-suggested)",
            "example": 3
          },
          "picketWidthIn": {
            "type": "number",
            "description": "Width of individual vertical pickets in inches",
            "default": 5.5,
            "example": 5.5
          },
          "picketGapIn": {
            "type": "number",
            "description": "Spacing gap between pickets in inches (0 for solid privacy)",
            "default": 0,
            "example": 0
          },
          "panelWidthFt": {
            "type": "number",
            "description": "Width of prefabricated panels in feet (for panel styles)",
            "default": 8,
            "example": 8
          },
          "gateCount": {
            "type": "integer",
            "description": "Number of gate openings",
            "default": 1,
            "example": 1
          },
          "gateWidthFt": {
            "type": "number",
            "description": "Width of each gate in feet",
            "default": 4,
            "example": 4
          },
          "cornerCount": {
            "type": "integer",
            "description": "Number of 90-degree corner posts",
            "default": 4,
            "example": 4
          },
          "endCount": {
            "type": "integer",
            "description": "Number of terminal end posts",
            "default": 2,
            "example": 2
          },
          "postHoleDepthIn": {
            "type": "number",
            "description": "Depth of post holes in inches",
            "default": 24,
            "example": 24
          },
          "postHoleDiameterIn": {
            "type": "number",
            "description": "Diameter of post holes in inches",
            "default": 10,
            "example": 10
          },
          "wasteFactorPercent": {
            "type": "number",
            "description": "Waste allowance percentage",
            "default": 10,
            "example": 10
          }
        }
      },
      "FenceCalculationResponse": {
        "type": "object",
        "required": [
          "posts",
          "rails",
          "concrete",
          "summary"
        ],
        "properties": {
          "posts": {
            "type": "object",
            "required": [
              "linePosts",
              "cornerPosts",
              "endPosts",
              "gatePosts",
              "totalPosts",
              "recommendedLengthFt",
              "recommendedSizeIn"
            ],
            "properties": {
              "linePosts": {
                "type": "integer",
                "example": 14
              },
              "cornerPosts": {
                "type": "integer",
                "example": 4
              },
              "endPosts": {
                "type": "integer",
                "example": 2
              },
              "gatePosts": {
                "type": "integer",
                "example": 2
              },
              "totalPosts": {
                "type": "integer",
                "example": 22
              },
              "recommendedLengthFt": {
                "type": "number",
                "example": 8
              },
              "recommendedSizeIn": {
                "type": "string",
                "example": "4x4"
              }
            }
          },
          "rails": {
            "type": "object",
            "required": [
              "sections",
              "railsPerSection",
              "totalRails"
            ],
            "properties": {
              "sections": {
                "type": "integer",
                "example": 19
              },
              "railsPerSection": {
                "type": "integer",
                "example": 3
              },
              "totalRails": {
                "type": "integer",
                "example": 57
              }
            }
          },
          "pickets": {
            "type": "object",
            "properties": {
              "picketPitchIn": {
                "type": "number",
                "example": 5.5
              },
              "rawCount": {
                "type": "integer",
                "example": 319
              },
              "wasteCount": {
                "type": "integer",
                "example": 32
              },
              "totalCount": {
                "type": "integer",
                "example": 351
              },
              "picketsPerLinearFoot": {
                "type": "number",
                "example": 2.18
              }
            }
          },
          "panels": {
            "type": "object",
            "properties": {
              "panelWidthFt": {
                "type": "number",
                "example": 8
              },
              "fullPanels": {
                "type": "integer",
                "example": 18
              },
              "cutPanels": {
                "type": "integer",
                "example": 1
              },
              "totalPanels": {
                "type": "integer",
                "example": 21
              }
            }
          },
          "concrete": {
            "type": "object",
            "required": [
              "perHoleVolumeFt3",
              "totalVolumeFt3",
              "cubicYards",
              "bags50lb",
              "bags60lb",
              "bags80lb"
            ],
            "properties": {
              "perHoleVolumeFt3": {
                "type": "number",
                "example": 1.091
              },
              "totalVolumeFt3": {
                "type": "number",
                "example": 24
              },
              "cubicYards": {
                "type": "number",
                "example": 0.889
              },
              "bags50lb": {
                "type": "integer",
                "example": 64
              },
              "bags60lb": {
                "type": "integer",
                "example": 54
              },
              "bags80lb": {
                "type": "integer",
                "example": 40
              }
            }
          },
          "summary": {
            "type": "object",
            "required": [
              "runLengthFt",
              "effectiveRunFt",
              "totalPosts",
              "totalRails",
              "concreteBags80lb"
            ],
            "properties": {
              "runLengthFt": {
                "type": "number",
                "example": 150
              },
              "effectiveRunFt": {
                "type": "number",
                "example": 146
              },
              "totalPosts": {
                "type": "integer",
                "example": 22
              },
              "totalRails": {
                "type": "integer",
                "example": 57
              },
              "totalPickets": {
                "type": "integer",
                "example": 351
              },
              "totalPanels": {
                "type": "integer"
              },
              "concreteBags80lb": {
                "type": "integer",
                "example": 40
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "resolution"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error identifier",
                "example": "INVALID_INPUT"
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation of what went wrong",
                "example": "lengthFt must be a positive number."
              },
              "resolution": {
                "type": "string",
                "description": "Direct actionable guidance for agents or callers to resolve the issue",
                "example": "Provide a valid lengthFt between 1 and 50,000 feet."
              }
            }
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "service",
          "version"
        ],
        "properties": {
          "status": {
            "type": "string",
            "example": "pass"
          },
          "service": {
            "type": "string",
            "example": "Fence Calculator API"
          },
          "version": {
            "type": "string",
            "example": "1.0.0"
          },
          "openapi": {
            "type": "string",
            "example": "/openapi.json"
          },
          "documentation": {
            "type": "string",
            "example": "https://fencecalculator.xyz/about"
          }
        }
      }
    }
  }
}