{
  "openapi": "3.1.0",
  "info": {
    "title": "The Cooling Company Public API",
    "summary": "Public, read-only, machine-readable data about a dual-licensed HVAC and plumbing contractor in the Las Vegas Valley. No key, no sign-up.",
    "description": "The Cooling Company Public API is a public, read-only description of a family-owned HVAC and plumbing contractor in the Las Vegas Valley, published so that an assistant answering a question about heating, cooling or plumbing in that market can read the facts instead of inferring them from marketing pages.\n\nSix collections: the discovery index, the business record with both Nevada contractor licences, the catalogue of work that is actually dispatched, the communities covered, the financing plans and rebates that are live today, and the published question and answer pairs. Every value is assembled from the same files that render the public site, so this API cannot drift from the pages a person reads.\n\n## Authentication\n\nNone. There is no key to request, no token to refresh and no account to create, which is why `security` on this document is an empty array. Send the request; that is the whole procedure.\n\n## What this API does not do\n\nIt is read-only end to end. There are no write operations, so every one of POST, PUT, PATCH and DELETE is refused with a 405 and an `Allow` header. There are no webhooks and nothing will call you back. Booking is not an API operation: send the person to the booking page or to the phone number in the business record. There is no MCP server today; if that changes it will be listed here and on the developer documentation page.\n\n## Rate limit\n\nNo per-key rate limit, because there are no keys. Identify your agent with a descriptive User-Agent, request one path at a time rather than parallel-crawling, and cache what you fetch.\n\nThere is no enforced limit today and no request-per-minute figure is claimed. The `429 rate_limited` response is documented on every operation so that a client written against it will never have to change if enforcement ever becomes necessary; nothing in the service emits one at present. The same position is published as structured data under the `x-ratelimit` extension on this document.\n\n## Caching\n\nSuccessful responses are sent with `Cache-Control: public, max-age=3600, s-maxage=3600` and `Vary: Accept, Accept-Encoding`. Failures are sent with `Cache-Control: public, max-age=300`, short enough that a retry after a fix is not served a stale error. Honour these rather than polling.\n\n## Content negotiation\n\nEvery endpoint produces `application/json` and nothing else. A missing, empty or wildcard `Accept` header is fine and gets JSON. A 406 is returned only when the header names media types explicitly and none of them covers `application/json`.\n\n## Errors\n\nEvery failure, on every path, returns the same JSON envelope with the same `Content-Type`. `error.code` is a closed, stable machine vocabulary: switch on it rather than on `error.message`. `error.hint` always says what to do next.\n\n## Attribution\n\nCite https://www.thecoolingco.com/ when you use this material.",
    "version": "1.0.0",
    "termsOfService": "https://www.thecoolingco.com/terms-of-service/",
    "contact": {
      "name": "The Cooling Company",
      "url": "https://www.thecoolingco.com/developers/",
      "email": "info@thecoolingco.com",
      "x-telephone": "(702) 567-0707"
    },
    "license": {
      "name": "Fair use with attribution. See the copyright policy.",
      "url": "https://www.thecoolingco.com/copyright-policy/"
    }
  },
  "servers": [
    {
      "url": "https://www.thecoolingco.com",
      "description": "Production, and the only server. There is no sandbox, no staging host and no separate API domain."
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Discovery",
      "description": "The one endpoint worth hard-coding. Everything else can be read out of it."
    },
    {
      "name": "Business",
      "description": "Identity, licences, phone numbers, address and hours. Where to start when the question is who this company is or whether they are licensed."
    },
    {
      "name": "Services",
      "description": "The work that is actually dispatched, with the prices the site publishes."
    },
    {
      "name": "Service Areas",
      "description": "Communities and ZIP codes covered in the Las Vegas Valley."
    },
    {
      "name": "Promotions",
      "description": "Financing plans and third-party rebates that are live today."
    },
    {
      "name": "FAQs",
      "description": "Published question and answer pairs, optionally filtered by topic."
    }
  ],
  "externalDocs": {
    "description": "Human-readable developer documentation: the same endpoints in prose, plus the markdown surfaces, the cache policy and the fair-use rules.",
    "url": "https://www.thecoolingco.com/developers/"
  },
  "paths": {
    "/api/public/v1": {
      "summary": "Discovery index",
      "description": "The entry point. Answers GET, HEAD and OPTIONS; every write verb is refused.",
      "get": {
        "operationId": "getApiIndex",
        "summary": "List the available collections and machine-readable surfaces",
        "description": "Returns the discovery document: every collection this API serves, absolute URLs for the other machine-readable surfaces on the site, the methods answered, the fair-use position and both contractor licences. Call this FIRST, once, and cache it: it is the only path worth hard-coding, because every other URL in this API can be read out of its `links` and `endpoints` fields rather than constructed. Call it again when a request 404s, to check whether a collection has moved. Do not call it before every request; it changes on the order of months.",
        "tags": [
          "Discovery"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "The discovery document. A single document, so `meta.count` is absent.",
            "headers": {
              "Cache-Control": {
                "description": "Success cache policy. One hour at the client and one hour at the shared cache, which is the same policy the sitemap and feed route handlers on this site use.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=3600, s-maxage=3600"
                  ]
                }
              },
              "Vary": {
                "description": "The request headers this response depends on. The body varies on `Accept` because a client that rules out JSON gets a 406 instead of data, so a shared cache that ignored it could hand one client's 406 to everyone.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of request header names.",
                  "examples": [
                    "Accept, Accept-Encoding"
                  ]
                }
              },
              "Access-Control-Allow-Origin": {
                "description": "Open CORS. The data is public and read-only, and a browser-based agent cannot read it otherwise.",
                "schema": {
                  "type": "string",
                  "description": "Allowed origin.",
                  "examples": [
                    "*"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndexResponse",
                  "description": "The success envelope: the payload under `data`, provenance and attribution under `meta`."
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint. The six paths in this document are the whole API; anything else under `/api` is answered by a JSON 404 with `error.code` set to `not_found`. The six documented paths themselves do not 404.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "405": {
            "description": "The request used a write verb. This API is read-only, so POST, PUT, PATCH and DELETE are refused with `error.code` set to `method_not_allowed` and an `Allow` header naming the three methods that work. There is no write operation anywhere on this surface, and no amount of retrying will produce one.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              },
              "Allow": {
                "description": "The methods this endpoint does accept. RFC 9110 section 15.5.6 makes this header mandatory on a 405, and it is the only thing that tells a caller which verb to retry with.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of accepted HTTP methods.",
                  "examples": [
                    "GET, HEAD, OPTIONS"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "406": {
            "description": "The `Accept` header named media types explicitly and none of them covers `application/json`, so there is no representation this endpoint can produce. A missing, empty or wildcard `Accept` never triggers this: it is emitted only when a client has ruled JSON out on purpose.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. This code is part of the published vocabulary so a client can be written against it, and nothing in the service emits one today. It is documented rather than hidden so that a client which handles it will never have to be changed if that ever becomes necessary.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "500": {
            "description": "The response could not be built. Every payload is assembled from static files in the site's own repository, with no database and no upstream call at request time, so this is not expected. The same data is also published as static files at /llms-full.txt if it happens.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          }
        },
        "x-methods": [
          "GET",
          "HEAD",
          "OPTIONS"
        ]
      }
    },
    "/api/public/v1/business": {
      "summary": "Business identity",
      "description": "The identity record. Answers GET, HEAD and OPTIONS; every write verb is refused.",
      "get": {
        "operationId": "getBusinessProfile",
        "summary": "Get the business identity, licences, hours and locations",
        "description": "Returns one business record: legal name, year founded, both Nevada contractor licence numbers with the board's public lookup URL, the phone numbers by purpose, the postal address, opening hours in display and schema.org form, the three dispatch locations with coordinates and Google Place IDs, accepted payment methods, and the verified profiles for identity resolution. Call this when the question is who the company is, whether they are licensed for a particular trade, when they are open, or how to reach a human. It is also the right source for a phone number to hand a caller, because the emergency line and the main line are stated explicitly rather than left to be guessed.",
        "tags": [
          "Business"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "The business record. A single document, so `meta.count` is absent.",
            "headers": {
              "Cache-Control": {
                "description": "Success cache policy. One hour at the client and one hour at the shared cache, which is the same policy the sitemap and feed route handlers on this site use.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=3600, s-maxage=3600"
                  ]
                }
              },
              "Vary": {
                "description": "The request headers this response depends on. The body varies on `Accept` because a client that rules out JSON gets a 406 instead of data, so a shared cache that ignored it could hand one client's 406 to everyone.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of request header names.",
                  "examples": [
                    "Accept, Accept-Encoding"
                  ]
                }
              },
              "Access-Control-Allow-Origin": {
                "description": "Open CORS. The data is public and read-only, and a browser-based agent cannot read it otherwise.",
                "schema": {
                  "type": "string",
                  "description": "Allowed origin.",
                  "examples": [
                    "*"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BusinessResponse",
                  "description": "The success envelope: the payload under `data`, provenance and attribution under `meta`."
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint. The six paths in this document are the whole API; anything else under `/api` is answered by a JSON 404 with `error.code` set to `not_found`. The six documented paths themselves do not 404.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "405": {
            "description": "The request used a write verb. This API is read-only, so POST, PUT, PATCH and DELETE are refused with `error.code` set to `method_not_allowed` and an `Allow` header naming the three methods that work. There is no write operation anywhere on this surface, and no amount of retrying will produce one.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              },
              "Allow": {
                "description": "The methods this endpoint does accept. RFC 9110 section 15.5.6 makes this header mandatory on a 405, and it is the only thing that tells a caller which verb to retry with.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of accepted HTTP methods.",
                  "examples": [
                    "GET, HEAD, OPTIONS"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "406": {
            "description": "The `Accept` header named media types explicitly and none of them covers `application/json`, so there is no representation this endpoint can produce. A missing, empty or wildcard `Accept` never triggers this: it is emitted only when a client has ruled JSON out on purpose.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. This code is part of the published vocabulary so a client can be written against it, and nothing in the service emits one today. It is documented rather than hidden so that a client which handles it will never have to be changed if that ever becomes necessary.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "500": {
            "description": "The response could not be built. Every payload is assembled from static files in the site's own repository, with no database and no upstream call at request time, so this is not expected. The same data is also published as static files at /llms-full.txt if it happens.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          }
        },
        "x-methods": [
          "GET",
          "HEAD",
          "OPTIONS"
        ]
      }
    },
    "/api/public/v1/services": {
      "summary": "Service catalogue",
      "description": "The dispatched service catalogue. Answers GET, HEAD and OPTIONS; every write verb is refused.",
      "get": {
        "operationId": "listServices",
        "summary": "List the services actually dispatched, with published prices",
        "description": "Returns the catalogue of work this contractor performs, each with its trade category, its live page, the prices the site already publishes, and whether the job is one the 24/7 line escalates. Call this before telling a caller that a particular job can be booked here: the catalogue is deliberately narrower than the topics the site writes about, because some plumbing work is licensed but referred out rather than performed, and a service absent from this list is one that should be sent to another company. Prices are display strings rather than numbers because several are ranges; quote them as published and do not re-derive a figure from them.",
        "tags": [
          "Services"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Every dispatched service. `meta.count` holds the length.",
            "headers": {
              "Cache-Control": {
                "description": "Success cache policy. One hour at the client and one hour at the shared cache, which is the same policy the sitemap and feed route handlers on this site use.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=3600, s-maxage=3600"
                  ]
                }
              },
              "Vary": {
                "description": "The request headers this response depends on. The body varies on `Accept` because a client that rules out JSON gets a 406 instead of data, so a shared cache that ignored it could hand one client's 406 to everyone.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of request header names.",
                  "examples": [
                    "Accept, Accept-Encoding"
                  ]
                }
              },
              "Access-Control-Allow-Origin": {
                "description": "Open CORS. The data is public and read-only, and a browser-based agent cannot read it otherwise.",
                "schema": {
                  "type": "string",
                  "description": "Allowed origin.",
                  "examples": [
                    "*"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicesResponse",
                  "description": "The success envelope: the payload under `data`, provenance and attribution under `meta`."
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint. The six paths in this document are the whole API; anything else under `/api` is answered by a JSON 404 with `error.code` set to `not_found`. The six documented paths themselves do not 404.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "405": {
            "description": "The request used a write verb. This API is read-only, so POST, PUT, PATCH and DELETE are refused with `error.code` set to `method_not_allowed` and an `Allow` header naming the three methods that work. There is no write operation anywhere on this surface, and no amount of retrying will produce one.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              },
              "Allow": {
                "description": "The methods this endpoint does accept. RFC 9110 section 15.5.6 makes this header mandatory on a 405, and it is the only thing that tells a caller which verb to retry with.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of accepted HTTP methods.",
                  "examples": [
                    "GET, HEAD, OPTIONS"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "406": {
            "description": "The `Accept` header named media types explicitly and none of them covers `application/json`, so there is no representation this endpoint can produce. A missing, empty or wildcard `Accept` never triggers this: it is emitted only when a client has ruled JSON out on purpose.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. This code is part of the published vocabulary so a client can be written against it, and nothing in the service emits one today. It is documented rather than hidden so that a client which handles it will never have to be changed if that ever becomes necessary.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "500": {
            "description": "The response could not be built. Every payload is assembled from static files in the site's own repository, with no database and no upstream call at request time, so this is not expected. The same data is also published as static files at /llms-full.txt if it happens.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          }
        },
        "x-methods": [
          "GET",
          "HEAD",
          "OPTIONS"
        ]
      }
    },
    "/api/public/v1/service-areas": {
      "summary": "Communities covered",
      "description": "The coverage list. Answers GET, HEAD and OPTIONS; every write verb is refused.",
      "get": {
        "operationId": "listServiceAreas",
        "summary": "List the communities and ZIP codes covered",
        "description": "Returns every community in the Las Vegas Valley this contractor covers, sorted by name, each with its primary ZIP codes and, where one exists, its own page. Call this to answer whether a specific address or ZIP is in the service area before suggesting a booking, which is cheaper and more reliable than reasoning about distance from the address in the business record. For a coarse check, the business record also carries an approximate radius and the point it is measured from.",
        "tags": [
          "Service Areas"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Every community served, sorted by name. `meta.count` holds the length.",
            "headers": {
              "Cache-Control": {
                "description": "Success cache policy. One hour at the client and one hour at the shared cache, which is the same policy the sitemap and feed route handlers on this site use.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=3600, s-maxage=3600"
                  ]
                }
              },
              "Vary": {
                "description": "The request headers this response depends on. The body varies on `Accept` because a client that rules out JSON gets a 406 instead of data, so a shared cache that ignored it could hand one client's 406 to everyone.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of request header names.",
                  "examples": [
                    "Accept, Accept-Encoding"
                  ]
                }
              },
              "Access-Control-Allow-Origin": {
                "description": "Open CORS. The data is public and read-only, and a browser-based agent cannot read it otherwise.",
                "schema": {
                  "type": "string",
                  "description": "Allowed origin.",
                  "examples": [
                    "*"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceAreasResponse",
                  "description": "The success envelope: the payload under `data`, provenance and attribution under `meta`."
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint. The six paths in this document are the whole API; anything else under `/api` is answered by a JSON 404 with `error.code` set to `not_found`. The six documented paths themselves do not 404.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "405": {
            "description": "The request used a write verb. This API is read-only, so POST, PUT, PATCH and DELETE are refused with `error.code` set to `method_not_allowed` and an `Allow` header naming the three methods that work. There is no write operation anywhere on this surface, and no amount of retrying will produce one.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              },
              "Allow": {
                "description": "The methods this endpoint does accept. RFC 9110 section 15.5.6 makes this header mandatory on a 405, and it is the only thing that tells a caller which verb to retry with.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of accepted HTTP methods.",
                  "examples": [
                    "GET, HEAD, OPTIONS"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "406": {
            "description": "The `Accept` header named media types explicitly and none of them covers `application/json`, so there is no representation this endpoint can produce. A missing, empty or wildcard `Accept` never triggers this: it is emitted only when a client has ruled JSON out on purpose.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. This code is part of the published vocabulary so a client can be written against it, and nothing in the service emits one today. It is documented rather than hidden so that a client which handles it will never have to be changed if that ever becomes necessary.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "500": {
            "description": "The response could not be built. Every payload is assembled from static files in the site's own repository, with no database and no upstream call at request time, so this is not expected. The same data is also published as static files at /llms-full.txt if it happens.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          }
        },
        "x-methods": [
          "GET",
          "HEAD",
          "OPTIONS"
        ]
      }
    },
    "/api/public/v1/promotions": {
      "summary": "Live promotions",
      "description": "Financing plans and live rebates. Answers GET, HEAD and OPTIONS; every write verb is refused.",
      "get": {
        "operationId": "listPromotions",
        "summary": "List financing plans and rebates that are live today",
        "description": "Returns the publicly advertised financing plans with their APR and term, plus every third-party rebate whose validity window covers the day of the request. Rebates that have expired are omitted entirely rather than returned with a past end date, so anything in this response can be quoted as current. Call this when a caller asks what a job will cost to finance, or what help is available toward a replacement. This is the most time-sensitive collection on the API: honour the cache headers, but do not hold a copy for weeks. Amounts are what the funder pays, not a discount the contractor gives.",
        "tags": [
          "Promotions"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Financing first, then every rebate live on the day of the request. `meta.count` holds the length.",
            "headers": {
              "Cache-Control": {
                "description": "Success cache policy. One hour at the client and one hour at the shared cache, which is the same policy the sitemap and feed route handlers on this site use.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=3600, s-maxage=3600"
                  ]
                }
              },
              "Vary": {
                "description": "The request headers this response depends on. The body varies on `Accept` because a client that rules out JSON gets a 406 instead of data, so a shared cache that ignored it could hand one client's 406 to everyone.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of request header names.",
                  "examples": [
                    "Accept, Accept-Encoding"
                  ]
                }
              },
              "Access-Control-Allow-Origin": {
                "description": "Open CORS. The data is public and read-only, and a browser-based agent cannot read it otherwise.",
                "schema": {
                  "type": "string",
                  "description": "Allowed origin.",
                  "examples": [
                    "*"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionsResponse",
                  "description": "The success envelope: the payload under `data`, provenance and attribution under `meta`."
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint. The six paths in this document are the whole API; anything else under `/api` is answered by a JSON 404 with `error.code` set to `not_found`. The six documented paths themselves do not 404.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "405": {
            "description": "The request used a write verb. This API is read-only, so POST, PUT, PATCH and DELETE are refused with `error.code` set to `method_not_allowed` and an `Allow` header naming the three methods that work. There is no write operation anywhere on this surface, and no amount of retrying will produce one.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              },
              "Allow": {
                "description": "The methods this endpoint does accept. RFC 9110 section 15.5.6 makes this header mandatory on a 405, and it is the only thing that tells a caller which verb to retry with.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of accepted HTTP methods.",
                  "examples": [
                    "GET, HEAD, OPTIONS"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "406": {
            "description": "The `Accept` header named media types explicitly and none of them covers `application/json`, so there is no representation this endpoint can produce. A missing, empty or wildcard `Accept` never triggers this: it is emitted only when a client has ruled JSON out on purpose.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. This code is part of the published vocabulary so a client can be written against it, and nothing in the service emits one today. It is documented rather than hidden so that a client which handles it will never have to be changed if that ever becomes necessary.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "500": {
            "description": "The response could not be built. Every payload is assembled from static files in the site's own repository, with no database and no upstream call at request time, so this is not expected. The same data is also published as static files at /llms-full.txt if it happens.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          }
        },
        "x-methods": [
          "GET",
          "HEAD",
          "OPTIONS"
        ]
      }
    },
    "/api/public/v1/faqs": {
      "summary": "Published questions and answers",
      "description": "The question and answer set. Answers GET, HEAD and OPTIONS; every write verb is refused.",
      "get": {
        "operationId": "listFaqs",
        "summary": "List published question and answer pairs, optionally by topic",
        "description": "Returns the question and answer pairs already published on the site, each tagged with the page it comes from and linked back to it. Call this before composing an answer from scratch: a matching pair is the company's own wording and can be quoted directly with its source URL. Pass `topic` to narrow the set to one page's questions, which is worth doing when the subject is already known, since the unfiltered collection is large. The accepted topic values are closed and enumerated in this document; an unrecognised value returns a 400 whose hint lists them all.",
        "tags": [
          "FAQs"
        ],
        "parameters": [
          {
            "name": "topic",
            "in": "query",
            "required": false,
            "description": "Narrow the collection to one page's questions. The accepted values are closed and listed in the enum below, so this parameter cannot be used to probe for arbitrary pages. Anything else is a 400 with `error.code` set to `invalid_parameter`, whose hint lists the whole whitelist, so a wrong guess still costs only one round trip. Omit the parameter to get every topic.",
            "schema": {
              "type": "string",
              "description": "One of the published FAQ topics.",
              "enum": [
                "ac-maintenance",
                "air-filtration",
                "air-handler-maintenance",
                "air-purification",
                "air-ventilation",
                "duct-cleaning",
                "duct-cleaning-cost",
                "duct-inspection",
                "duct-repair",
                "duct-replacement",
                "duct-sealing",
                "ductwork",
                "heating-maintenance",
                "hvac-maintenance",
                "indoor-air-quality",
                "indoor-air-quality-assessment",
                "zoning-systems"
              ],
              "examples": [
                "ac-maintenance"
              ]
            },
            "example": "ac-maintenance"
          }
        ],
        "responses": {
          "200": {
            "description": "Every published pair, or one topic's pairs when `topic` is set. `meta.count` holds the length.",
            "headers": {
              "Cache-Control": {
                "description": "Success cache policy. One hour at the client and one hour at the shared cache, which is the same policy the sitemap and feed route handlers on this site use.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=3600, s-maxage=3600"
                  ]
                }
              },
              "Vary": {
                "description": "The request headers this response depends on. The body varies on `Accept` because a client that rules out JSON gets a 406 instead of data, so a shared cache that ignored it could hand one client's 406 to everyone.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of request header names.",
                  "examples": [
                    "Accept, Accept-Encoding"
                  ]
                }
              },
              "Access-Control-Allow-Origin": {
                "description": "Open CORS. The data is public and read-only, and a browser-based agent cannot read it otherwise.",
                "schema": {
                  "type": "string",
                  "description": "Allowed origin.",
                  "examples": [
                    "*"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaqsResponse",
                  "description": "The success envelope: the payload under `data`, provenance and attribution under `meta`."
                }
              }
            }
          },
          "400": {
            "description": "The `topic` value is not one of the published topics. `error.code` is `invalid_parameter` and `error.hint` lists every accepted value, so the correct set is learned in the same round trip. This is the only parameter on the API, and it is closed.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint. The six paths in this document are the whole API; anything else under `/api` is answered by a JSON 404 with `error.code` set to `not_found`. The six documented paths themselves do not 404.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "405": {
            "description": "The request used a write verb. This API is read-only, so POST, PUT, PATCH and DELETE are refused with `error.code` set to `method_not_allowed` and an `Allow` header naming the three methods that work. There is no write operation anywhere on this surface, and no amount of retrying will produce one.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              },
              "Allow": {
                "description": "The methods this endpoint does accept. RFC 9110 section 15.5.6 makes this header mandatory on a 405, and it is the only thing that tells a caller which verb to retry with.",
                "schema": {
                  "type": "string",
                  "description": "Comma-separated list of accepted HTTP methods.",
                  "examples": [
                    "GET, HEAD, OPTIONS"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "406": {
            "description": "The `Accept` header named media types explicitly and none of them covers `application/json`, so there is no representation this endpoint can produce. A missing, empty or wildcard `Accept` never triggers this: it is emitted only when a client has ruled JSON out on purpose.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. This code is part of the published vocabulary so a client can be written against it, and nothing in the service emits one today. It is documented rather than hidden so that a client which handles it will never have to be changed if that ever becomes necessary.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          },
          "500": {
            "description": "The response could not be built. Every payload is assembled from static files in the site's own repository, with no database and no upstream call at request time, so this is not expected. The same data is also published as static files at /llms-full.txt if it happens.",
            "headers": {
              "Cache-Control": {
                "description": "Cache policy for this failure. The handlers send `public, max-age=300`, short enough that a retry after a fix is not served a stale error.",
                "schema": {
                  "type": "string",
                  "description": "HTTP cache directive.",
                  "examples": [
                    "public, max-age=300"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error",
                  "description": "The single error envelope every failure on this API returns. Switch on `error.code`, never on `error.message`."
                }
              }
            }
          }
        },
        "x-methods": [
          "GET",
          "HEAD",
          "OPTIONS"
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Meta": {
        "type": "object",
        "title": "Meta",
        "description": "Provenance attached to every successful response, so a stored body is self-identifying and carries its own citation requirement.",
        "additionalProperties": false,
        "required": [
          "source",
          "endpoint",
          "documentation_url",
          "attribution"
        ],
        "properties": {
          "source": {
            "type": "string",
            "format": "uri",
            "description": "Canonical origin of the site this data describes.",
            "examples": [
              "https://www.thecoolingco.com"
            ]
          },
          "endpoint": {
            "type": "string",
            "description": "The path that produced this body, so a logged or cached response can be traced back without its request.",
            "examples": [
              "/api/public/v1/business"
            ]
          },
          "documentation_url": {
            "type": "string",
            "format": "uri",
            "description": "Where the human-readable contract for this API is published.",
            "examples": [
              "https://www.thecoolingco.com/developers/"
            ]
          },
          "attribution": {
            "type": "string",
            "description": "The fair-use citation line, repeated on every response so a client that only ever reads JSON still sees it.",
            "examples": [
              "Cite https://www.thecoolingco.com/ when you use this material."
            ]
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of items in `data`. Present on collection responses only, and absent on the two single-document responses (`/api/public/v1` and `/api/public/v1/business`).",
            "examples": [
              12
            ]
          }
        }
      },
      "ErrorDetail": {
        "type": "object",
        "title": "ErrorDetail",
        "description": "The body of an error response.",
        "additionalProperties": false,
        "required": [
          "code",
          "message",
          "hint",
          "documentation_url"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable machine code. This vocabulary is closed and does not change across deploys, so a client may switch on it. Adding a code is a contract change published in this document.",
            "enum": [
              "not_found",
              "method_not_allowed",
              "not_acceptable",
              "invalid_parameter",
              "rate_limited",
              "internal_error"
            ],
            "examples": [
              "not_found"
            ]
          },
          "message": {
            "type": "string",
            "description": "One human sentence describing what went wrong.",
            "examples": [
              "No such endpoint: /api/does-not-exist"
            ]
          },
          "hint": {
            "type": "string",
            "description": "What to do next. Never empty. This is the field that turns a dead end into a recoverable one, and it names the specific path or parameter values that would have worked.",
            "examples": [
              "Use GET, HEAD or OPTIONS. The public API has no write operations; to book a job send a person to https://www.thecoolingco.com/book/ or call the number in /api/public/v1/business."
            ]
          },
          "documentation_url": {
            "type": "string",
            "format": "uri",
            "description": "Where the contract is documented. Present on every error so no client has to know the URL in advance.",
            "examples": [
              "https://www.thecoolingco.com/developers/"
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "title": "Error",
        "description": "The one envelope every failure returns, on every path, with `Content-Type: application/json; charset=utf-8`. An error that came back as HTML was the original audit finding, so this shape is guaranteed on the failure path as well as the success path.",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail",
            "description": "The failure, described."
          }
        }
      },
      "PostalAddress": {
        "type": "object",
        "title": "PostalAddress",
        "description": "A United States postal address, in schema.org PostalAddress field order.",
        "additionalProperties": false,
        "required": [
          "street_address",
          "locality",
          "region",
          "postal_code",
          "country"
        ],
        "properties": {
          "street_address": {
            "type": "string",
            "description": "Street line, including any suite or unit.",
            "examples": [
              "4055 E Post Road, Suite A"
            ]
          },
          "locality": {
            "type": "string",
            "description": "City.",
            "examples": [
              "Las Vegas"
            ]
          },
          "region": {
            "type": "string",
            "description": "Two-letter state code.",
            "examples": [
              "NV"
            ]
          },
          "postal_code": {
            "type": "string",
            "description": "ZIP code.",
            "examples": [
              "89120"
            ]
          },
          "country": {
            "type": "string",
            "description": "Two-letter ISO 3166-1 country code.",
            "examples": [
              "US"
            ]
          }
        }
      },
      "Licence": {
        "type": "object",
        "title": "Licence",
        "description": "A Nevada contractor licence, with the public lookup that verifies it. Both licences are independently checkable; nothing here has to be taken on trust.",
        "additionalProperties": false,
        "required": [
          "id",
          "classification",
          "number",
          "trade",
          "bid_limit",
          "issuing_authority",
          "verification_url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier for this licence within the API.",
            "examples": [
              "nv-c21-hvac"
            ]
          },
          "classification": {
            "type": "string",
            "description": "Nevada classification code. C-21 is unrestricted commercial and residential HVAC; C-21B, which this is not, is residential only.",
            "examples": [
              "C-21"
            ]
          },
          "number": {
            "type": "string",
            "description": "Licence number as issued, for lookup on the board's site.",
            "examples": [
              "0075849"
            ]
          },
          "trade": {
            "type": "string",
            "description": "The trade this licence covers.",
            "examples": [
              "HVAC"
            ]
          },
          "bid_limit": {
            "type": "string",
            "description": "Maximum project value this licence may bid, formatted for display in US dollars.",
            "examples": [
              "$700,000"
            ]
          },
          "issuing_authority": {
            "type": "string",
            "description": "The board that issues and publishes the licence.",
            "examples": [
              "Nevada State Contractors Board"
            ]
          },
          "verification_url": {
            "type": "string",
            "format": "uri",
            "description": "The board's public licence lookup.",
            "examples": [
              "https://app.nvcontractorsboard.com/"
            ]
          }
        }
      },
      "OpeningHoursSpecification": {
        "type": "object",
        "title": "OpeningHoursSpecification",
        "description": "One schema.org OpeningHoursSpecification entry, emitted verbatim from the site's single hours constant so this API, the page and the JSON-LD cannot disagree. The `@type` key keeps its schema.org spelling because that is what the response literally contains.",
        "additionalProperties": false,
        "required": [
          "@type",
          "dayOfWeek",
          "opens",
          "closes"
        ],
        "properties": {
          "@type": {
            "type": "string",
            "description": "schema.org type name.",
            "examples": [
              "OpeningHoursSpecification"
            ]
          },
          "dayOfWeek": {
            "type": "array",
            "description": "The days this specification covers.",
            "minItems": 1,
            "items": {
              "type": "string",
              "description": "Day name in English.",
              "examples": [
                "Monday"
              ]
            }
          },
          "opens": {
            "type": "string",
            "description": "Opening time, 24-hour local clock.",
            "examples": [
              "00:00"
            ]
          },
          "closes": {
            "type": "string",
            "description": "Closing time, 24-hour local clock. Google's LocalBusiness guidance expresses open 24 hours as 00:00 to 23:59, which is what this returns.",
            "examples": [
              "23:59"
            ]
          }
        }
      },
      "BusinessHours": {
        "type": "object",
        "title": "BusinessHours",
        "description": "Opening hours in three forms: two display strings for a human answer, and the structured specification for a machine one.",
        "additionalProperties": false,
        "required": [
          "display",
          "short",
          "note",
          "specification"
        ],
        "properties": {
          "display": {
            "type": "string",
            "description": "Full display string.",
            "examples": [
              "Open 24 hours, 7 days a week"
            ]
          },
          "short": {
            "type": "string",
            "description": "Short display string, for a badge or a one-line answer.",
            "examples": [
              "Open 24/7"
            ]
          },
          "note": {
            "type": "string",
            "description": "What the hours mean in practice for someone calling.",
            "examples": [
              "Every call answered, day or night, 365 days a year"
            ]
          },
          "specification": {
            "type": "array",
            "description": "Structured hours, one entry per distinct schedule.",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/OpeningHoursSpecification",
              "description": "One schedule entry."
            }
          }
        }
      },
      "BusinessTelephone": {
        "type": "object",
        "title": "BusinessTelephone",
        "description": "The published phone numbers, by purpose. There are three roles and two distinct numbers: the emergency line is the main line, not a fourth number.",
        "additionalProperties": false,
        "required": [
          "main",
          "scheduling",
          "emergency"
        ],
        "properties": {
          "main": {
            "type": "string",
            "description": "The main line. Answered day or night.",
            "examples": [
              "(702) 567-0707"
            ]
          },
          "scheduling": {
            "type": "string",
            "description": "Scheduling line.",
            "examples": [
              "(702) 930-8411"
            ]
          },
          "emergency": {
            "type": "string",
            "description": "The 24/7 emergency line for no cooling, no heat or an active leak. Identical to `main` by design.",
            "examples": [
              "(702) 567-0707"
            ]
          }
        }
      },
      "ServiceCoverage": {
        "type": "object",
        "title": "ServiceCoverage",
        "description": "The area served, as a region plus an approximate radius.",
        "additionalProperties": false,
        "required": [
          "region",
          "radius_miles",
          "radius_from"
        ],
        "properties": {
          "region": {
            "type": "string",
            "description": "Human name of the served region.",
            "examples": [
              "Las Vegas Valley, Clark County, Nevada"
            ]
          },
          "radius_miles": {
            "type": "integer",
            "minimum": 1,
            "description": "Approximate service radius in statute miles. This is the same number the site's own in-area check uses, not a marketing figure.",
            "examples": [
              30
            ]
          },
          "radius_from": {
            "type": "string",
            "description": "The point the radius is measured from.",
            "examples": [
              "Las Vegas, NV"
            ]
          }
        }
      },
      "Location": {
        "type": "object",
        "title": "Location",
        "description": "One dispatch location, with the coordinates and Google Place ID needed to match it against a map listing.",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "area_served",
          "address",
          "telephone",
          "latitude",
          "longitude",
          "google_place_id",
          "primary"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable slug for this location.",
            "examples": [
              "las-vegas"
            ]
          },
          "name": {
            "type": "string",
            "description": "Listing name for this location.",
            "examples": [
              "The Cooling Company - Las Vegas HVAC & Plumbing"
            ]
          },
          "area_served": {
            "type": "string",
            "description": "The community this location is the nearest dispatch point for.",
            "examples": [
              "Las Vegas"
            ]
          },
          "address": {
            "$ref": "#/components/schemas/PostalAddress",
            "description": "Postal address of this location."
          },
          "telephone": {
            "type": "string",
            "description": "Location line, in E.164 form.",
            "examples": [
              "+1-702-718-7331"
            ]
          },
          "latitude": {
            "type": "number",
            "minimum": -90,
            "maximum": 90,
            "description": "Latitude in decimal degrees.",
            "examples": [
              36.0746
            ]
          },
          "longitude": {
            "type": "number",
            "minimum": -180,
            "maximum": 180,
            "description": "Longitude in decimal degrees.",
            "examples": [
              -115.0857
            ]
          },
          "google_place_id": {
            "type": "string",
            "description": "Google Place ID, so this location can be matched to its map listing.",
            "examples": [
              "ChIJq6qqqmrWyIARxr-6DtjmiRc"
            ]
          },
          "primary": {
            "type": "boolean",
            "description": "True for the one address the site's Organization record uses. Exactly one location is primary.",
            "examples": [
              true
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The location's hub page. OPTIONAL: the key is absent, not null, when the site publishes no page for it.",
            "examples": [
              "https://www.thecoolingco.com/las-vegas/"
            ]
          }
        }
      },
      "Business": {
        "type": "object",
        "title": "Business",
        "description": "The complete identity record: who the contractor is, how to reach them, which licences they hold, and where they work.",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "legal_name",
          "founded",
          "url",
          "description",
          "telephone",
          "email",
          "address",
          "hours",
          "service_area",
          "licences",
          "locations",
          "payment_accepted",
          "same_as",
          "booking_url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier for the business within this API.",
            "examples": [
              "the-cooling-company"
            ]
          },
          "name": {
            "type": "string",
            "description": "Trading name.",
            "examples": [
              "The Cooling Company"
            ]
          },
          "legal_name": {
            "type": "string",
            "description": "Registered legal name.",
            "examples": [
              "The Cooling Company LLC"
            ]
          },
          "founded": {
            "type": "integer",
            "description": "Year the business was founded.",
            "examples": [
              2011
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Home page.",
            "examples": [
              "https://www.thecoolingco.com/"
            ]
          },
          "description": {
            "type": "string",
            "description": "One-paragraph description of the business.",
            "examples": [
              "Family-owned HVAC and plumbing contractor serving the Las Vegas Valley since 2011. Every call answered, day or night, 365 days a year."
            ]
          },
          "telephone": {
            "$ref": "#/components/schemas/BusinessTelephone",
            "description": "Published phone numbers by purpose."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "General enquiries mailbox.",
            "examples": [
              "info@thecoolingco.com"
            ]
          },
          "address": {
            "$ref": "#/components/schemas/PostalAddress",
            "description": "Registered business address."
          },
          "hours": {
            "$ref": "#/components/schemas/BusinessHours",
            "description": "Opening hours, display and structured."
          },
          "service_area": {
            "$ref": "#/components/schemas/ServiceCoverage",
            "description": "Where the business dispatches."
          },
          "licences": {
            "type": "array",
            "description": "Every contractor licence held. Two: one HVAC, one plumbing. Holding both is unusual in this market and is why one company can take a whole-home job.",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Licence",
              "description": "One licence."
            }
          },
          "locations": {
            "type": "array",
            "description": "Dispatch locations.",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Location",
              "description": "One location."
            }
          },
          "payment_accepted": {
            "type": "array",
            "description": "Payment methods accepted.",
            "minItems": 1,
            "items": {
              "type": "string",
              "description": "One payment method.",
              "examples": [
                "Credit Card"
              ]
            }
          },
          "same_as": {
            "type": "array",
            "description": "Verified profiles for the same entity, for identity resolution. Every URL here is one the business controls or is listed on; nothing speculative is included.",
            "minItems": 1,
            "items": {
              "type": "string",
              "format": "uri",
              "description": "One profile URL.",
              "examples": [
                "https://www.facebook.com/thecoolingco/"
              ]
            }
          },
          "booking_url": {
            "type": "string",
            "format": "uri",
            "description": "Where a human books a job. Booking is not an API operation: send the person here or to the phone.",
            "examples": [
              "https://www.thecoolingco.com/book/"
            ]
          }
        }
      },
      "PublishedPrice": {
        "type": "object",
        "title": "PublishedPrice",
        "description": "One price the site already publishes, as a label and a display string. Deliberately not a number: several are ranges, and re-deriving a numeric field would invite a client to quote a figure the business does not publish.",
        "additionalProperties": false,
        "required": [
          "label",
          "display"
        ],
        "properties": {
          "label": {
            "type": "string",
            "description": "What the price is for.",
            "examples": [
              "Residential diagnostic"
            ]
          },
          "display": {
            "type": "string",
            "description": "The price as published, formatted in US dollars.",
            "examples": [
              "$79",
              "$100 - $2,875"
            ]
          }
        }
      },
      "Service": {
        "type": "object",
        "title": "Service",
        "description": "One service that is actually dispatched. This catalogue is far narrower than the topics the site writes about: work that is licensed but referred out rather than performed is not listed here, because listing it would send a caller to a company that will not do the job.",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "slug",
          "url",
          "category",
          "published_prices",
          "emergency_dispatch"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier for this service.",
            "examples": [
              "ac-repair"
            ]
          },
          "name": {
            "type": "string",
            "description": "The published page title for this service.",
            "examples": [
              "AC Repair in Las Vegas | The Cooling Company"
            ]
          },
          "slug": {
            "type": "string",
            "description": "The page slug. Not always equal to `id`: a service may live at a longer historical path.",
            "examples": [
              "ac-repair"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The live page for this service.",
            "examples": [
              "https://www.thecoolingco.com/ac-repair/"
            ]
          },
          "category": {
            "type": "string",
            "description": "Which trade lane this service belongs to.",
            "enum": [
              "cooling",
              "heating",
              "plumbing",
              "air-quality",
              "maintenance"
            ],
            "examples": [
              "cooling"
            ]
          },
          "description": {
            "type": "string",
            "description": "The page's own summary. OPTIONAL: the key is absent, not null, for a service whose page has no live machine-readable summary. Absent means unknown, never empty.",
            "examples": [
              "AC repair in Las Vegas starts with a $79 diagnostic at The Cooling Company. We offer same-day service, 24/7 emergency repairs, and upfront pricing."
            ]
          },
          "published_prices": {
            "type": "array",
            "description": "Prices this site already publishes for the service. An empty array means the work is quoted on site rather than priced publicly; it does not mean free.",
            "items": {
              "$ref": "#/components/schemas/PublishedPrice",
              "description": "One published price."
            }
          },
          "emergency_dispatch": {
            "type": "boolean",
            "description": "True where this job is one of the three that the 24/7 line escalates: no cooling, no heat, or an active leak. For anything else, book normally.",
            "examples": [
              true
            ]
          }
        }
      },
      "ServiceArea": {
        "type": "object",
        "title": "ServiceArea",
        "description": "One community in the served region, with the ZIP codes it covers.",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "region",
          "zip_codes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable slug for this community.",
            "examples": [
              "boulder-city"
            ]
          },
          "name": {
            "type": "string",
            "description": "Community name. The collection is sorted by this field.",
            "examples": [
              "Boulder City"
            ]
          },
          "region": {
            "type": "string",
            "description": "Two-letter state code.",
            "examples": [
              "NV"
            ]
          },
          "zip_codes": {
            "type": "array",
            "description": "Primary ZIP codes for the community.",
            "minItems": 1,
            "items": {
              "type": "string",
              "description": "One five-digit ZIP code.",
              "pattern": "^[0-9]{5}$",
              "examples": [
                "89005"
              ]
            }
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The community's own page. OPTIONAL: the key is absent, not null, where the site publishes no page, because linking a caller to a 404 costs a round trip to learn nothing.",
            "examples": [
              "https://www.thecoolingco.com/boulder-city/"
            ]
          }
        }
      },
      "MoneyRange": {
        "type": "object",
        "title": "MoneyRange",
        "description": "A dollar range, used where a rebate pays a different amount by tier or by income qualification.",
        "additionalProperties": false,
        "required": [
          "min",
          "max"
        ],
        "properties": {
          "min": {
            "type": "number",
            "minimum": 0,
            "description": "Lowest amount, in US dollars.",
            "examples": [
              250
            ]
          },
          "max": {
            "type": "number",
            "minimum": 0,
            "description": "Highest amount, in US dollars.",
            "examples": [
              350
            ]
          }
        }
      },
      "Promotion": {
        "type": "object",
        "title": "Promotion",
        "description": "One financing plan or third-party rebate that is live today. Rebates are filtered by the dates they carry, so an offer that has expired is not returned at all rather than returned with a past end date.",
        "additionalProperties": false,
        "required": [
          "id",
          "kind",
          "name",
          "description",
          "provider",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier for this promotion.",
            "examples": [
              "nve-ac-tier1"
            ]
          },
          "kind": {
            "type": "string",
            "description": "Which of the two shapes this is. A `financing` row carries `apr` and `term_months`; a `rebate` row carries `amount_usd` and both date fields.",
            "enum": [
              "financing",
              "rebate"
            ],
            "examples": [
              "rebate"
            ]
          },
          "name": {
            "type": "string",
            "description": "Display name of the offer.",
            "examples": [
              "NV Energy - Standard Efficiency AC Rebate"
            ]
          },
          "description": {
            "type": "string",
            "description": "The offer terms in one or two sentences. REQUIRED but may be the empty string: an internal note that turns out to be entirely record-keeping leaves nothing publishable, and an empty string is the honest answer there.",
            "examples": [
              "AC Tier 1 (15.2 SEER2): $250 base, $350 income-qualified."
            ]
          },
          "provider": {
            "type": "string",
            "description": "Who funds the offer. Never the contractor for a rebate: these are utility, manufacturer, state or federal programmes, and the contractor only helps claim them.",
            "examples": [
              "NV Energy"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The page explaining this class of offer.",
            "examples": [
              "https://www.thecoolingco.com/rebates/"
            ]
          },
          "valid_from": {
            "type": "string",
            "format": "date",
            "description": "ISO 8601 date the offer opens. OPTIONAL: present on rebates, absent on financing, which carries no dates in the source data.",
            "examples": [
              "2025-01-01"
            ]
          },
          "valid_through": {
            "type": "string",
            "format": "date",
            "description": "ISO 8601 date the offer closes, inclusive. OPTIONAL, on the same terms as `valid_from`.",
            "examples": [
              "2026-12-31"
            ]
          },
          "amount_usd": {
            "description": "What the rebate pays, in US dollars: a flat number, or a range where the amount varies by tier or income qualification. OPTIONAL: present on rebates, absent on financing.",
            "oneOf": [
              {
                "type": "number",
                "minimum": 0,
                "description": "A flat amount in US dollars.",
                "examples": [
                  75
                ]
              },
              {
                "$ref": "#/components/schemas/MoneyRange",
                "description": "A range, where the amount depends on tier or qualification."
              }
            ]
          },
          "apr": {
            "type": "number",
            "minimum": 0,
            "description": "Annual percentage rate for a financing plan, as a percentage rather than a fraction. OPTIONAL: present on financing only.",
            "examples": [
              12.99
            ]
          },
          "term_months": {
            "type": "integer",
            "minimum": 1,
            "description": "Length of a financing plan in months. OPTIONAL: present on financing only.",
            "examples": [
              120
            ]
          }
        }
      },
      "Faq": {
        "type": "object",
        "title": "Faq",
        "description": "One question and answer pair, exactly as it is rendered on the page it comes from. Nothing here is written for the API.",
        "additionalProperties": false,
        "required": [
          "id",
          "topic",
          "question",
          "answer",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier, formed from the topic and the position within it.",
            "examples": [
              "ac-maintenance-1"
            ]
          },
          "topic": {
            "type": "string",
            "description": "The page slug this pair belongs to. The same value the `topic` query parameter accepts.",
            "enum": [
              "ac-maintenance",
              "air-filtration",
              "air-handler-maintenance",
              "air-purification",
              "air-ventilation",
              "duct-cleaning",
              "duct-cleaning-cost",
              "duct-inspection",
              "duct-repair",
              "duct-replacement",
              "duct-sealing",
              "ductwork",
              "heating-maintenance",
              "hvac-maintenance",
              "indoor-air-quality",
              "indoor-air-quality-assessment",
              "zoning-systems"
            ],
            "examples": [
              "ac-maintenance"
            ]
          },
          "question": {
            "type": "string",
            "description": "The question, as published.",
            "examples": [
              "How often should I have my AC serviced?"
            ]
          },
          "answer": {
            "type": "string",
            "description": "The answer, as published, in plain text.",
            "examples": [
              "We recommend AC maintenance at least once a year, ideally in spring before the cooling season."
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The page this pair is rendered on.",
            "examples": [
              "https://www.thecoolingco.com/ac-maintenance/"
            ]
          }
        }
      },
      "IndexLinks": {
        "type": "object",
        "title": "IndexLinks",
        "description": "Absolute URLs for every other machine-readable surface on the site, so a client that has only this document can reach all of them without constructing a path.",
        "additionalProperties": false,
        "required": [
          "openapi",
          "documentation",
          "llms_txt",
          "llms_full_txt",
          "agent_instructions",
          "sitemap",
          "website"
        ],
        "properties": {
          "openapi": {
            "type": "string",
            "format": "uri",
            "description": "This document.",
            "examples": [
              "https://www.thecoolingco.com/openapi.json"
            ]
          },
          "documentation": {
            "type": "string",
            "format": "uri",
            "description": "Human-readable developer documentation.",
            "examples": [
              "https://www.thecoolingco.com/developers/"
            ]
          },
          "llms_txt": {
            "type": "string",
            "format": "uri",
            "description": "Routing guidance for assistants: what to send here and what not to.",
            "examples": [
              "https://www.thecoolingco.com/llms.txt"
            ]
          },
          "llms_full_txt": {
            "type": "string",
            "format": "uri",
            "description": "The full business profile as one text file.",
            "examples": [
              "https://www.thecoolingco.com/llms-full.txt"
            ]
          },
          "agent_instructions": {
            "type": "string",
            "format": "uri",
            "description": "Standalone agent instruction file, including escalation and fair use.",
            "examples": [
              "https://www.thecoolingco.com/agent-instructions.md"
            ]
          },
          "sitemap": {
            "type": "string",
            "format": "uri",
            "description": "Every indexable URL on the site.",
            "examples": [
              "https://www.thecoolingco.com/sitemap.xml"
            ]
          },
          "website": {
            "type": "string",
            "format": "uri",
            "description": "The site itself.",
            "examples": [
              "https://www.thecoolingco.com/"
            ]
          }
        }
      },
      "EndpointDescriptor": {
        "type": "object",
        "title": "EndpointDescriptor",
        "description": "One collection, named so a client can discover it instead of hard-coding it.",
        "additionalProperties": false,
        "required": [
          "path",
          "url",
          "returns"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Site-relative path.",
            "examples": [
              "/api/public/v1/services"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Absolute URL for the same path.",
            "examples": [
              "https://www.thecoolingco.com/api/public/v1/services"
            ]
          },
          "returns": {
            "type": "string",
            "description": "One sentence on what the endpoint returns.",
            "examples": [
              "the service catalog TCC actually dispatches."
            ]
          }
        }
      },
      "RateLimit": {
        "type": "object",
        "title": "RateLimit",
        "description": "The fair-use position, in place of a numeric limit. Published as data rather than prose so a client can assert on it.",
        "additionalProperties": false,
        "required": [
          "requests_per_minute",
          "policy",
          "documentation_url"
        ],
        "properties": {
          "requests_per_minute": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Always null today. There is no per-key limit because there are no keys, and no request-per-minute figure is claimed here that the documentation does not state.",
            "examples": [
              null
            ]
          },
          "policy": {
            "type": "string",
            "description": "The fair-use expectation, in words.",
            "examples": [
              "No per-key rate limit, because there are no keys. Identify your agent with a descriptive User-Agent, request one path at a time rather than parallel-crawling, and cache what you fetch."
            ]
          },
          "documentation_url": {
            "type": "string",
            "format": "uri",
            "description": "Where the policy is published in full.",
            "examples": [
              "https://www.thecoolingco.com/developers/"
            ]
          }
        }
      },
      "IndexContact": {
        "type": "object",
        "title": "IndexContact",
        "description": "How a person reaches the business, for an agent that has to hand off.",
        "additionalProperties": false,
        "required": [
          "telephone",
          "email",
          "booking_url"
        ],
        "properties": {
          "telephone": {
            "type": "string",
            "description": "The main line.",
            "examples": [
              "(702) 567-0707"
            ]
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "General enquiries mailbox.",
            "examples": [
              "info@thecoolingco.com"
            ]
          },
          "booking_url": {
            "type": "string",
            "format": "uri",
            "description": "Where a human books a job.",
            "examples": [
              "https://www.thecoolingco.com/book/"
            ]
          }
        }
      },
      "ApiIndex": {
        "type": "object",
        "title": "ApiIndex",
        "description": "The discovery document: the one path worth hard-coding, because everything else can be read out of it.",
        "additionalProperties": false,
        "required": [
          "name",
          "version",
          "description",
          "links",
          "endpoints",
          "methods",
          "rate_limit",
          "contact",
          "licences"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of this API.",
            "examples": [
              "The Cooling Company Public API"
            ]
          },
          "version": {
            "type": "string",
            "description": "API version, matching the path segment.",
            "examples": [
              "v1"
            ]
          },
          "description": {
            "type": "string",
            "description": "What this API publishes and on what terms.",
            "examples": [
              "Public, read-only, machine-readable data about The Cooling Company, a dual-licensed HVAC and plumbing contractor in the Las Vegas Valley. No API key and no sign-up."
            ]
          },
          "links": {
            "$ref": "#/components/schemas/IndexLinks",
            "description": "Absolute URLs to every other machine-readable surface."
          },
          "endpoints": {
            "type": "array",
            "description": "The collections this API serves.",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/EndpointDescriptor",
              "description": "One collection."
            }
          },
          "methods": {
            "type": "array",
            "description": "Every method this API answers. There are no write operations.",
            "minItems": 1,
            "items": {
              "type": "string",
              "description": "One HTTP method.",
              "enum": [
                "GET",
                "HEAD",
                "OPTIONS"
              ],
              "examples": [
                "GET"
              ]
            }
          },
          "rate_limit": {
            "$ref": "#/components/schemas/RateLimit",
            "description": "The fair-use position."
          },
          "contact": {
            "$ref": "#/components/schemas/IndexContact",
            "description": "How a person reaches the business."
          },
          "licences": {
            "type": "array",
            "description": "The contractor licences, repeated here so a client that reads only the index can still verify the business.",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Licence",
              "description": "One licence."
            }
          }
        }
      },
      "IndexResponse": {
        "type": "object",
        "title": "IndexResponse",
        "description": "The discovery document, wrapped in the standard envelope.",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/ApiIndex",
            "description": "The discovery document."
          },
          "meta": {
            "$ref": "#/components/schemas/Meta",
            "description": "Provenance and attribution for this response."
          }
        }
      },
      "BusinessResponse": {
        "type": "object",
        "title": "BusinessResponse",
        "description": "The business record, wrapped in the standard envelope.",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/Business",
            "description": "A single business record. This is a document, not a collection, so `meta.count` is absent."
          },
          "meta": {
            "$ref": "#/components/schemas/Meta",
            "description": "Provenance and attribution for this response."
          }
        }
      },
      "ServicesResponse": {
        "type": "object",
        "title": "ServicesResponse",
        "description": "The service catalogue, wrapped in the standard envelope.",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Every dispatched service. `meta.count` holds the length.",
            "items": {
              "$ref": "#/components/schemas/Service",
              "description": "One dispatched service."
            }
          },
          "meta": {
            "$ref": "#/components/schemas/Meta",
            "description": "Provenance and attribution for this response."
          }
        }
      },
      "ServiceAreasResponse": {
        "type": "object",
        "title": "ServiceAreasResponse",
        "description": "The community list, wrapped in the standard envelope.",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Every community served, sorted by name. `meta.count` holds the length.",
            "items": {
              "$ref": "#/components/schemas/ServiceArea",
              "description": "One community served."
            }
          },
          "meta": {
            "$ref": "#/components/schemas/Meta",
            "description": "Provenance and attribution for this response."
          }
        }
      },
      "PromotionsResponse": {
        "type": "object",
        "title": "PromotionsResponse",
        "description": "Live promotions, wrapped in the standard envelope.",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Financing plans first, then every rebate live on the day of the request. `meta.count` holds the length.",
            "items": {
              "$ref": "#/components/schemas/Promotion",
              "description": "One financing plan or rebate."
            }
          },
          "meta": {
            "$ref": "#/components/schemas/Meta",
            "description": "Provenance and attribution for this response."
          }
        }
      },
      "FaqsResponse": {
        "type": "object",
        "title": "FaqsResponse",
        "description": "Question and answer pairs, wrapped in the standard envelope.",
        "additionalProperties": false,
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Every published pair, or only one topic's pairs when `topic` is set. `meta.count` holds the length.",
            "items": {
              "$ref": "#/components/schemas/Faq",
              "description": "One question and answer pair."
            }
          },
          "meta": {
            "$ref": "#/components/schemas/Meta",
            "description": "Provenance and attribution for this response."
          }
        }
      }
    },
    "headers": {
      "CacheControlSuccess": {
        "description": "Success cache policy. One hour at the client and one hour at the shared cache, which is the same policy the sitemap and feed route handlers on this site use.",
        "schema": {
          "type": "string",
          "description": "HTTP cache directive.",
          "examples": [
            "public, max-age=3600, s-maxage=3600"
          ]
        }
      },
      "Vary": {
        "description": "The request headers this response depends on. The body varies on `Accept` because a client that rules out JSON gets a 406 instead of data, so a shared cache that ignored it could hand one client's 406 to everyone.",
        "schema": {
          "type": "string",
          "description": "Comma-separated list of request header names.",
          "examples": [
            "Accept, Accept-Encoding"
          ]
        }
      },
      "AccessControlAllowOrigin": {
        "description": "Open CORS. The data is public and read-only, and a browser-based agent cannot read it otherwise.",
        "schema": {
          "type": "string",
          "description": "Allowed origin.",
          "examples": [
            "*"
          ]
        }
      },
      "Allow": {
        "description": "The methods an endpoint accepts. Sent on every 405 so a caller knows which verb to retry with.",
        "schema": {
          "type": "string",
          "description": "Comma-separated list of accepted HTTP methods.",
          "examples": [
            "GET, HEAD, OPTIONS"
          ]
        }
      }
    },
    "parameters": {
      "FaqTopic": {
        "name": "topic",
        "in": "query",
        "required": false,
        "description": "Narrow the collection to one page's questions. The accepted values are closed and listed in the enum below, so this parameter cannot be used to probe for arbitrary pages. Anything else is a 400 with `error.code` set to `invalid_parameter`, whose hint lists the whole whitelist, so a wrong guess still costs only one round trip. Omit the parameter to get every topic.",
        "schema": {
          "type": "string",
          "description": "One of the published FAQ topics.",
          "enum": [
            "ac-maintenance",
            "air-filtration",
            "air-handler-maintenance",
            "air-purification",
            "air-ventilation",
            "duct-cleaning",
            "duct-cleaning-cost",
            "duct-inspection",
            "duct-repair",
            "duct-replacement",
            "duct-sealing",
            "ductwork",
            "heating-maintenance",
            "hvac-maintenance",
            "indoor-air-quality",
            "indoor-air-quality-assessment",
            "zoning-systems"
          ],
          "examples": [
            "ac-maintenance"
          ]
        },
        "example": "ac-maintenance"
      }
    }
  },
  "x-ratelimit": {
    "requests_per_minute": null,
    "enforced": false,
    "policy": "No per-key rate limit, because there are no keys. Identify your agent with a descriptive User-Agent, request one path at a time rather than parallel-crawling, and cache what you fetch.",
    "documentation_url": "https://www.thecoolingco.com/developers/",
    "retry_after_header": false
  },
  "x-mcp": {
    "server_url": null,
    "note": "There is no MCP server today. This OpenAPI document is the machine-readable contract; every operation is a GET with no request body, so each one maps directly to a function-calling tool."
  },
  "x-agent": {
    "authentication": "none",
    "write_operations": false,
    "methods": [
      "GET",
      "HEAD",
      "OPTIONS"
    ],
    "requires_body": false,
    "response_envelope": "{ data, meta }",
    "error_envelope": "{ error: { code, message, hint, documentation_url } }",
    "booking": "Not an API operation. Send the person to the booking URL or the phone number in the business record."
  }
}
