{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Installation Pros Quote Request API",
    "version": "1.0.0",
    "description": "A narrow public interface for customer-authorized installation quote requests. A successful response means the Installation Pros office received the request for human review; it does not confirm scope, price, availability, or an appointment. No API key is required.",
    "contact": {
      "name": "Installation Pros",
      "email": "installationprosoffice@gmail.com",
      "url": "https://www.installationprossocal.com/contact"
    },
    "x-versioning-policy": {
      "strategy": "Major versions appear in the URL path, for example /api/v1/.",
      "minimumSupportDays": 180,
      "deprecationSignals": [
        "Deprecation response header (RFC 9745)",
        "Link response header with deprecation and successor-version relations",
        "Sunset response header (RFC 8594) when a removal date is scheduled"
      ],
      "currentVersion": "v1",
      "legacyRoute": "/api/quote-request",
      "policyUrl": "https://www.installationprossocal.com/developers#versioning"
    }
  },
  "servers": [
    {
      "url": "https://www.installationprossocal.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Installation Pros developer resources",
    "url": "https://www.installationprossocal.com/developers"
  },
  "tags": [
    {
      "name": "Quote requests",
      "description": "Validate or submit customer-authorized quote requests."
    }
  ],
  "paths": {
    "/api/v1/quote-requests": {
      "get": {
        "tags": [
          "Quote requests"
        ],
        "summary": "Read the quote-request contract",
        "description": "Returns accepted fields, enum values, limits, business identity, and response details for agents and integrations.",
        "operationId": "getQuoteRequestContractV1",
        "responses": {
          "200": {
            "description": "Machine-readable quote-request contract.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteApiDiscovery"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Quote requests"
        ],
        "summary": "Submit a customer-authorized quote request",
        "description": "Sends a validated installation quote request to the Installation Pros office for human review. Call only after the customer authorizes submission and provides accurate contact details.",
        "operationId": "submitQuoteRequestV1",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The office received the customer-authorized request for human review. This is not a confirmed price or appointment.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, bot-trap input, or invalid quote fields.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "413": {
            "description": "The request body exceeds 32 KiB.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "415": {
            "description": "The content type is not application/json or multipart/form-data.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "The request contains an unsupported attachment field.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "The client exceeded the quote-submission rate limit.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "The configured delivery provider did not confirm receipt.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/quote-requests/validate": {
      "post": {
        "tags": [
          "Quote requests"
        ],
        "summary": "Validate a quote request without sending it",
        "description": "Checks a JSON quote-request payload against the production field rules without contacting the office or invoking a delivery provider.",
        "operationId": "validateQuoteRequestV1",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The payload is valid and was not submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON or invalid quote fields.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "413": {
            "description": "The request body exceeds 32 KiB.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "415": {
            "description": "The content type is not application/json.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/quote-request": {
      "get": {
        "tags": [
          "Quote requests"
        ],
        "summary": "Read the legacy quote-request contract",
        "description": "Deprecated compatibility route. Use GET /api/v1/quote-requests for the canonical versioned contract.",
        "operationId": "getLegacyQuoteRequestContract",
        "deprecated": true,
        "responses": {
          "200": {
            "description": "The canonical contract plus RFC 9745 deprecation metadata.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteApiDiscovery"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Quote requests"
        ],
        "summary": "Submit through the legacy compatibility route",
        "description": "Deprecated compatibility route with the same behavior as POST /api/v1/quote-requests. Migrate to the versioned route.",
        "operationId": "submitLegacyQuoteRequest",
        "deprecated": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The office received the customer-authorized request for human review. This is not a confirmed price or appointment.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteAccepted"
                }
              }
            }
          },
          "default": {
            "description": "A typed error plus deprecation metadata.",
            "headers": {
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "RateLimit": {
        "description": "Current quote-submit quota state using the current IETF structured-field format.",
        "schema": {
          "type": "string",
          "example": "\"quote-submit\";r=4;t=600"
        }
      },
      "RateLimitPolicy": {
        "description": "Quote-submit quota policy using the current IETF structured-field format.",
        "schema": {
          "type": "string",
          "example": "\"quote-submit\";q=5;w=600"
        }
      },
      "RateLimitLimit": {
        "description": "Compatibility maximum for clients using the earlier RateLimit field set.",
        "schema": {
          "type": "integer",
          "example": 5
        }
      },
      "RateLimitRemaining": {
        "description": "Compatibility count of remaining submissions in the current window.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "example": 4
        }
      },
      "RateLimitReset": {
        "description": "Compatibility seconds until the current window resets.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "example": 600
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying after HTTP 429.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "Deprecation": {
        "description": "RFC 9745 structured date identifying when the legacy route became deprecated.",
        "schema": {
          "type": "string",
          "example": "@1787961600"
        }
      },
      "Link": {
        "description": "Links to the deprecation policy and successor route.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "QuoteRequest": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "name",
          "phone",
          "applianceType",
          "city"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 100,
            "description": "Customer full name."
          },
          "phone": {
            "type": "string",
            "maxLength": 32,
            "description": "Customer US phone number containing 10 digits, or 11 digits beginning with 1."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Optional customer email address."
          },
          "applianceType": {
            "type": "string",
            "enum": [
              "Dishwasher",
              "Refrigerator / Freezer",
              "Oven, Range, or Cooktop",
              "Over-the-Range Microwave",
              "Range Hood / Vent Hood",
              "Washer / Dryer (Laundry)",
              "Trash Compactor / Specialty Appliance",
              "Water Heater",
              "Toilet",
              "Faucet and/or Sink",
              "Garbage Disposal",
              "Reverse Osmosis System",
              "Existing Appliance Utility Connection",
              "Commercial / Multi-Unit Project",
              "Other / Not Sure"
            ],
            "description": "Appliance, fixture, water-heater, or commercial service requested."
          },
          "jobType": {
            "type": "string",
            "enum": [
              "Replacement",
              "New Installation",
              "Commercial",
              "Remodel",
              "Not Sure Yet"
            ],
            "description": "Installation project category when known."
          },
          "city": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120,
            "description": "Job-site city or ZIP code."
          },
          "timing": {
            "type": "string",
            "maxLength": 100,
            "description": "Optional preferred timing."
          },
          "description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Optional model numbers, access details, existing connections, and other scope notes."
          }
        },
        "examples": [
          {
            "name": "Example Customer",
            "phone": "9095550100",
            "applianceType": "Dishwasher",
            "city": "Rancho Cucamonga",
            "jobType": "Replacement",
            "description": "Customer-authorized request for a standard replacement installation."
          }
        ]
      },
      "QuoteAccepted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "success",
          "message",
          "contact"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "message": {
            "type": "string"
          },
          "contact": {
            "$ref": "#/components/schemas/Contact"
          }
        }
      },
      "Contact": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "phone",
          "hours",
          "email"
        ],
        "properties": {
          "phone": {
            "type": "string"
          },
          "hours": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "ValidationResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "valid"
        ],
        "properties": {
          "valid": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "ProblemDetails": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "resolution"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri-reference"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "pattern": "^[A-Z][A-Z0-9_]*$"
          },
          "resolution": {
            "type": "string"
          },
          "error": {
            "type": "string",
            "description": "Backward-compatible alias of detail."
          }
        }
      },
      "QuoteApiDiscovery": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "service",
          "description",
          "endpoint",
          "method",
          "accepts",
          "required_fields",
          "optional_fields",
          "accepted_values",
          "limits",
          "attachments",
          "response",
          "business"
        ],
        "properties": {
          "service": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "endpoint": {
            "type": "string",
            "const": "/api/v1/quote-requests"
          },
          "method": {
            "type": "string",
            "const": "POST"
          },
          "accepts": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "multipart/form-data",
                "application/json"
              ]
            }
          },
          "required_fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "optional_fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "accepted_values": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "applianceType",
              "jobType"
            ],
            "properties": {
              "applianceType": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "Dishwasher",
                    "Refrigerator / Freezer",
                    "Oven, Range, or Cooktop",
                    "Over-the-Range Microwave",
                    "Range Hood / Vent Hood",
                    "Washer / Dryer (Laundry)",
                    "Trash Compactor / Specialty Appliance",
                    "Water Heater",
                    "Toilet",
                    "Faucet and/or Sink",
                    "Garbage Disposal",
                    "Reverse Osmosis System",
                    "Existing Appliance Utility Connection",
                    "Commercial / Multi-Unit Project",
                    "Other / Not Sure"
                  ]
                }
              },
              "jobType": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "Replacement",
                    "New Installation",
                    "Commercial",
                    "Remodel",
                    "Not Sure Yet"
                  ]
                }
              }
            }
          },
          "limits": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 1
            }
          },
          "attachments": {
            "type": "string"
          },
          "response": {
            "type": "object",
            "additionalProperties": true
          },
          "business": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "name",
              "licenses",
              "service_area"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "licenses": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "service_area": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
