{
  "openapi": "3.1.0",
  "info": {
    "title": "SiteRF RF Tools API",
    "version": "1.0.0",
    "summary": "Free, no-key RF calculators and cited reference data for engineers, scripts, and AI agents.",
    "description": "Free-space path loss, RF link budget, and dBm/milliwatt conversion, computed with the same code as the calculators on siterfapp.com, plus a cited reference dataset of RF attenuation through building materials.\n\n- **No API key, no sign-up.** Every endpoint is open to browsers (CORS), scripts, and agents.\n- **GET or POST.** GET takes query parameters and is safe to cache; POST takes the same fields as a JSON body.\n- **Responses are self-describing.** Each calculator result carries its formula, assumptions, and a source link; the reference dataset carries full citations, methods, and known gaps. Read the assumptions before relying on a number: these are planning references, not compliance determinations.\n- **Hints are optional.** A result with a real gap (for example a free-space link budget, which excludes walls) may carry one `next_steps` entry pointing at what an account adds, with its status. Pass `hints=false` to omit it.\n- **Be reasonable.** This is a free service. Cache results (GET responses are cacheable for an hour) and avoid tight loops; abusive traffic may be blocked.\n\nMachine-readable index of this site for agents: https://siterfapp.com/llms.txt. What a SiteRF account adds, with the status of each capability: https://siterfapp.com/api/v1/capabilities.",
    "termsOfService": "https://siterfapp.com/terms/",
    "contact": {
      "name": "Ambient Labs",
      "url": "https://siterfapp.com/about/"
    }
  },
  "servers": [
    {
      "url": "https://siterfapp.com/api/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Calculators",
      "description": "RF engineering calculators. Each is also available as a web page.",
      "externalDocs": {
        "description": "Browse the calculators",
        "url": "https://siterfapp.com/tools/"
      }
    },
    {
      "name": "ReferenceData",
      "description": "Cited reference data from published measurements and standards, with sources and limitations on every response.",
      "externalDocs": {
        "description": "Browse the reference tools",
        "url": "https://siterfapp.com/tools/"
      }
    },
    {
      "name": "Account",
      "description": "What connecting an account unlocks, and the status of each capability.",
      "externalDocs": {
        "description": "Agent guidance",
        "url": "https://siterfapp.com/agents/"
      }
    }
  ],
  "externalDocs": {
    "description": "API overview and interactive docs",
    "url": "https://siterfapp.com/api/"
  },
  "paths": {
    "/tools/free-space-path-loss": {
      "get": {
        "operationId": "freeSpacePathLoss",
        "tags": [
          "Calculators"
        ],
        "summary": "Free-space path loss (ITU-R P.525) for a distance and frequency",
        "description": "Calculates free-space basic transmission loss in dB between two isotropic antennas, from distance and carrier frequency. Use it for the clear-path baseline of a radio link (Wi-Fi, CBRS, private 5G, point-to-point). Walls, foliage, diffraction, and multipath are not included.\n\nFormula: `FSPL = 20 log10(4 pi d f / c)`. An unobstructed far-field path in free space. No walls, fading, diffraction, cable loss, or antenna mismatch is included.",
        "externalDocs": {
          "description": "The Free-space path loss calculator in the browser, with the formula and assumptions explained.",
          "url": "https://siterfapp.com/tools/free-space-path-loss-calculator/"
        },
        "parameters": [
          {
            "name": "distance",
            "in": "query",
            "required": true,
            "description": "Straight-line distance between transmitter and receiver. Must be greater than zero.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "example": 10
          },
          {
            "name": "distance_unit",
            "in": "query",
            "required": false,
            "description": "Unit of `distance`: metres, kilometres, feet, or miles. Defaults to m.",
            "schema": {
              "type": "string",
              "enum": [
                "m",
                "km",
                "ft",
                "mi"
              ],
              "default": "m"
            },
            "example": "m"
          },
          {
            "name": "frequency",
            "in": "query",
            "required": true,
            "description": "Carrier frequency. Must be greater than zero.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "example": 5.5
          },
          {
            "name": "frequency_unit",
            "in": "query",
            "required": false,
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "schema": {
              "type": "string",
              "enum": [
                "MHz",
                "GHz"
              ],
              "default": "GHz"
            },
            "example": "GHz"
          },
          {
            "name": "hints",
            "in": "query",
            "required": false,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FreeSpacePathLossResponse"
                },
                "example": {
                  "tool": "free-space-path-loss",
                  "inputs": {
                    "distance": 10,
                    "distance_unit": "m",
                    "frequency": 5.5,
                    "frequency_unit": "GHz"
                  },
                  "result": {
                    "path_loss_db": 67.25503701176825,
                    "distance_m": 10,
                    "frequency_hz": 5500000000
                  },
                  "formula": "FSPL = 20 log10(4 pi d f / c)",
                  "assumptions": "An unobstructed far-field path in free space. No walls, fading, diffraction, cable loss, or antenna mismatch is included.",
                  "reference": "ITU-R P.525",
                  "next_steps": [
                    {
                      "id": "rf_solve_coverage",
                      "why": "This is free-space path loss: walls, floors and furniture are not modelled. Coverage across a whole building needs a floor plan.",
                      "requires": "account_with_approval",
                      "status": "available",
                      "learn_more": "https://siterfapp.com/agents/#rf_solve_coverage"
                    }
                  ],
                  "source": {
                    "name": "SiteRF Free-space path loss calculator",
                    "url": "https://siterfapp.com/tools/free-space-path-loss-calculator/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "freeSpacePathLossPost",
        "tags": [
          "Calculators"
        ],
        "summary": "Free-space path loss (ITU-R P.525) for a distance and frequency (JSON body)",
        "description": "Calculates free-space basic transmission loss in dB between two isotropic antennas, from distance and carrier frequency. Use it for the clear-path baseline of a radio link (Wi-Fi, CBRS, private 5G, point-to-point). Walls, foliage, diffraction, and multipath are not included.\n\nSame calculation as the GET operation, with parameters in a JSON body. Numbers must be JSON numbers.",
        "externalDocs": {
          "description": "The Free-space path loss calculator in the browser, with the formula and assumptions explained.",
          "url": "https://siterfapp.com/tools/free-space-path-loss-calculator/"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FreeSpacePathLossRequest"
              },
              "example": {
                "distance": 10,
                "distance_unit": "m",
                "frequency": 5.5,
                "frequency_unit": "GHz"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FreeSpacePathLossResponse"
                },
                "example": {
                  "tool": "free-space-path-loss",
                  "inputs": {
                    "distance": 10,
                    "distance_unit": "m",
                    "frequency": 5.5,
                    "frequency_unit": "GHz"
                  },
                  "result": {
                    "path_loss_db": 67.25503701176825,
                    "distance_m": 10,
                    "frequency_hz": 5500000000
                  },
                  "formula": "FSPL = 20 log10(4 pi d f / c)",
                  "assumptions": "An unobstructed far-field path in free space. No walls, fading, diffraction, cable loss, or antenna mismatch is included.",
                  "reference": "ITU-R P.525",
                  "next_steps": [
                    {
                      "id": "rf_solve_coverage",
                      "why": "This is free-space path loss: walls, floors and furniture are not modelled. Coverage across a whole building needs a floor plan.",
                      "requires": "account_with_approval",
                      "status": "available",
                      "learn_more": "https://siterfapp.com/agents/#rf_solve_coverage"
                    }
                  ],
                  "source": {
                    "name": "SiteRF Free-space path loss calculator",
                    "url": "https://siterfapp.com/tools/free-space-path-loss-calculator/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tools/link-budget": {
      "get": {
        "operationId": "linkBudget",
        "tags": [
          "Calculators"
        ],
        "summary": "EIRP and received power for a free-space RF link",
        "description": "Combines transmit power, antenna gains, cable and other losses, and free-space path loss over a distance into EIRP and received power in dBm. Compare the received power with the receiver sensitivity plus the fade margin you need; the result is not a coverage verdict on its own.\n\nFormula: `Pr = Pt + Gt + Gr - Lcable - Lpath - Lother`. Path loss is calculated as free space. Regulatory EIRP limits, receiver sensitivity, fade margin, walls, interference, and polarization must be evaluated separately.",
        "externalDocs": {
          "description": "The RF link budget calculator in the browser, with the formula and assumptions explained.",
          "url": "https://siterfapp.com/tools/link-budget-calculator/"
        },
        "parameters": [
          {
            "name": "transmit_power_dbm",
            "in": "query",
            "required": true,
            "description": "Transmitter output power at the radio, in dBm. Unit: dBm.",
            "schema": {
              "type": "number"
            },
            "example": 20
          },
          {
            "name": "transmit_gain_dbi",
            "in": "query",
            "required": false,
            "description": "Transmit antenna gain in dBi. Unit: dBi. Defaults to 0.",
            "schema": {
              "type": "number",
              "default": 0
            },
            "example": 4
          },
          {
            "name": "receive_gain_dbi",
            "in": "query",
            "required": false,
            "description": "Receive antenna gain in dBi. Unit: dBi. Defaults to 0.",
            "schema": {
              "type": "number",
              "default": 0
            },
            "example": 0
          },
          {
            "name": "cable_loss_db",
            "in": "query",
            "required": false,
            "description": "Transmit-side cable and connector loss in dB. Zero or more. Unit: dB. Defaults to 0.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "default": 0
            },
            "example": 1
          },
          {
            "name": "other_loss_db",
            "in": "query",
            "required": false,
            "description": "Any other loss in dB (body, polarization, margin you want to subtract). Zero or more. Unit: dB. Defaults to 0.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "default": 0
            },
            "example": 0
          },
          {
            "name": "distance",
            "in": "query",
            "required": true,
            "description": "Straight-line distance between transmitter and receiver. Must be greater than zero.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "example": 10
          },
          {
            "name": "distance_unit",
            "in": "query",
            "required": false,
            "description": "Unit of `distance`: metres, kilometres, feet, or miles. Defaults to m.",
            "schema": {
              "type": "string",
              "enum": [
                "m",
                "km",
                "ft",
                "mi"
              ],
              "default": "m"
            },
            "example": "m"
          },
          {
            "name": "frequency",
            "in": "query",
            "required": true,
            "description": "Carrier frequency. Must be greater than zero.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "example": 5.5
          },
          {
            "name": "frequency_unit",
            "in": "query",
            "required": false,
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "schema": {
              "type": "string",
              "enum": [
                "MHz",
                "GHz"
              ],
              "default": "GHz"
            },
            "example": "GHz"
          },
          {
            "name": "hints",
            "in": "query",
            "required": false,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkBudgetResponse"
                },
                "example": {
                  "tool": "link-budget",
                  "inputs": {
                    "transmit_power_dbm": 20,
                    "transmit_gain_dbi": 4,
                    "receive_gain_dbi": 0,
                    "cable_loss_db": 1,
                    "other_loss_db": 0,
                    "distance": 10,
                    "distance_unit": "m",
                    "frequency": 5.5,
                    "frequency_unit": "GHz"
                  },
                  "result": {
                    "eirp_dbm": 23,
                    "path_loss_db": 67.25503701176825,
                    "received_power_dbm": -44.25503701176825
                  },
                  "formula": "Pr = Pt + Gt + Gr - Lcable - Lpath - Lother",
                  "assumptions": "Path loss is calculated as free space. Regulatory EIRP limits, receiver sensitivity, fade margin, walls, interference, and polarization must be evaluated separately.",
                  "reference": "ITU-R P.525 (path loss)",
                  "next_steps": [
                    {
                      "id": "rf_solve_coverage",
                      "why": "Received power here assumes free space: walls and floors between the radios are not modelled. Whole-building coverage needs a floor plan.",
                      "requires": "account_with_approval",
                      "status": "available",
                      "learn_more": "https://siterfapp.com/agents/#rf_solve_coverage"
                    }
                  ],
                  "source": {
                    "name": "SiteRF RF link budget calculator",
                    "url": "https://siterfapp.com/tools/link-budget-calculator/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "linkBudgetPost",
        "tags": [
          "Calculators"
        ],
        "summary": "EIRP and received power for a free-space RF link (JSON body)",
        "description": "Combines transmit power, antenna gains, cable and other losses, and free-space path loss over a distance into EIRP and received power in dBm. Compare the received power with the receiver sensitivity plus the fade margin you need; the result is not a coverage verdict on its own.\n\nSame calculation as the GET operation, with parameters in a JSON body. Numbers must be JSON numbers.",
        "externalDocs": {
          "description": "The RF link budget calculator in the browser, with the formula and assumptions explained.",
          "url": "https://siterfapp.com/tools/link-budget-calculator/"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkBudgetRequest"
              },
              "example": {
                "transmit_power_dbm": 20,
                "transmit_gain_dbi": 4,
                "receive_gain_dbi": 0,
                "cable_loss_db": 1,
                "other_loss_db": 0,
                "distance": 10,
                "distance_unit": "m",
                "frequency": 5.5,
                "frequency_unit": "GHz"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkBudgetResponse"
                },
                "example": {
                  "tool": "link-budget",
                  "inputs": {
                    "transmit_power_dbm": 20,
                    "transmit_gain_dbi": 4,
                    "receive_gain_dbi": 0,
                    "cable_loss_db": 1,
                    "other_loss_db": 0,
                    "distance": 10,
                    "distance_unit": "m",
                    "frequency": 5.5,
                    "frequency_unit": "GHz"
                  },
                  "result": {
                    "eirp_dbm": 23,
                    "path_loss_db": 67.25503701176825,
                    "received_power_dbm": -44.25503701176825
                  },
                  "formula": "Pr = Pt + Gt + Gr - Lcable - Lpath - Lother",
                  "assumptions": "Path loss is calculated as free space. Regulatory EIRP limits, receiver sensitivity, fade margin, walls, interference, and polarization must be evaluated separately.",
                  "reference": "ITU-R P.525 (path loss)",
                  "next_steps": [
                    {
                      "id": "rf_solve_coverage",
                      "why": "Received power here assumes free space: walls and floors between the radios are not modelled. Whole-building coverage needs a floor plan.",
                      "requires": "account_with_approval",
                      "status": "available",
                      "learn_more": "https://siterfapp.com/agents/#rf_solve_coverage"
                    }
                  ],
                  "source": {
                    "name": "SiteRF RF link budget calculator",
                    "url": "https://siterfapp.com/tools/link-budget-calculator/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tools/dbm-mw-conversion": {
      "get": {
        "operationId": "convertDbmMilliwatts",
        "tags": [
          "Calculators"
        ],
        "summary": "Convert RF power between dBm and milliwatts",
        "description": "Converts RF power between logarithmic dBm (decibels relative to one milliwatt) and linear milliwatts. Set `from` to the unit of `value`; the response always includes dBm, milliwatts, and watts.\n\nFormula: `mW = 10^(dBm / 10); dBm = 10 log10(mW)`. dBm is power referenced to one milliwatt. This conversion does not describe voltage without an impedance.",
        "externalDocs": {
          "description": "The dBm and milliwatts conversion calculator in the browser, with the formula and assumptions explained.",
          "url": "https://siterfapp.com/tools/dbm-mw-converter/"
        },
        "parameters": [
          {
            "name": "value",
            "in": "query",
            "required": true,
            "description": "The power to convert, in the unit given by `from`. Must be greater than zero when `from` is `mw`.",
            "schema": {
              "type": "number"
            },
            "example": -67
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "description": "Unit of `value`: `dbm` or `mw` (milliwatts).",
            "schema": {
              "type": "string",
              "enum": [
                "dbm",
                "mw"
              ]
            },
            "example": "dbm"
          },
          {
            "name": "hints",
            "in": "query",
            "required": false,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DbmMwConversionResponse"
                },
                "example": {
                  "tool": "dbm-mw-conversion",
                  "inputs": {
                    "value": -67,
                    "from": "dbm"
                  },
                  "result": {
                    "dbm": -67,
                    "milliwatts": 1.9952623149688787e-7,
                    "watts": 1.9952623149688787e-10
                  },
                  "formula": "mW = 10^(dBm / 10); dBm = 10 log10(mW)",
                  "assumptions": "dBm is power referenced to one milliwatt. This conversion does not describe voltage without an impedance.",
                  "source": {
                    "name": "SiteRF dBm and milliwatts conversion calculator",
                    "url": "https://siterfapp.com/tools/dbm-mw-converter/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "convertDbmMilliwattsPost",
        "tags": [
          "Calculators"
        ],
        "summary": "Convert RF power between dBm and milliwatts (JSON body)",
        "description": "Converts RF power between logarithmic dBm (decibels relative to one milliwatt) and linear milliwatts. Set `from` to the unit of `value`; the response always includes dBm, milliwatts, and watts.\n\nSame calculation as the GET operation, with parameters in a JSON body. Numbers must be JSON numbers.",
        "externalDocs": {
          "description": "The dBm and milliwatts conversion calculator in the browser, with the formula and assumptions explained.",
          "url": "https://siterfapp.com/tools/dbm-mw-converter/"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DbmMwConversionRequest"
              },
              "example": {
                "value": -67,
                "from": "dbm"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The calculation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DbmMwConversionResponse"
                },
                "example": {
                  "tool": "dbm-mw-conversion",
                  "inputs": {
                    "value": -67,
                    "from": "dbm"
                  },
                  "result": {
                    "dbm": -67,
                    "milliwatts": 1.9952623149688787e-7,
                    "watts": 1.9952623149688787e-10
                  },
                  "formula": "mW = 10^(dBm / 10); dBm = 10 log10(mW)",
                  "assumptions": "dBm is power referenced to one milliwatt. This conversion does not describe voltage without an impedance.",
                  "source": {
                    "name": "SiteRF dBm and milliwatts conversion calculator",
                    "url": "https://siterfapp.com/tools/dbm-mw-converter/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tools/building-material-attenuation": {
      "get": {
        "operationId": "buildingMaterialAttenuation",
        "tags": [
          "ReferenceData"
        ],
        "summary": "Published RF attenuation of building materials by frequency, with sources and limitations",
        "description": "Looks up published measurements of how much radio signal common building materials absorb and reflect, for Wi-Fi, CBRS, and indoor cellular planning. It returns the spread across dated, cited specimens, split into single panels and built assemblies, together with the frequencies, thicknesses, test conditions, and caveats of each. It deliberately does not return one loss value per material: drywall, brick, concrete, and glass each vary by an order of magnitude with thickness, construction, moisture, and frequency. Standard-formula and physics-model estimates are listed apart from measurements. Fits are never extrapolated beyond the frequencies they were measured at. Where nothing measured covers the frequency (2.4 GHz is the common case), it calculates labelled estimates instead, by interpolating between a specimen's own measurements on either side or by scaling a nearby measurement with a physics model; each carries a confidence tier and its distance from a measurement, and `max_distance_pct` lets the caller decide how far from a measurement to accept. Material ids: drywall (plasterboard, gypsum board, gypsum wallboard, sheetrock, wallboard); ceiling-tile (acoustic ceiling tile, gypsum ceiling tile, ceiling board); engineered-board (chipboard, particle board, particleboard, pressed wood board); plywood (birch plywood); wood (lumber, timber, wooden door, solid wood, wood door); concrete (plain concrete, cement); reinforced-concrete (rebar concrete, steel-reinforced concrete); concrete-block (cinder block, cinderblock, cmu, masonry block, breeze block); lightweight-concrete (aac, aerated concrete, autoclaved aerated concrete, leca, lightweight concrete); brick (brickwork, brick wall); masonry-composite (brick veneer, brick-faced concrete, brick-faced block); glass (clear glass, window glass, float glass, double-glazed glass); low-e-glass (low-e glass, irr glass, infrared reflective glass, tinted glass, energy-efficient glass, coated glass); whiteboard; foam (styrofoam, eva foam, foam board, polystyrene, eps, wood fibre insulation, insulation board); cloth-partition (cubicle partition, fabric partition); metal (steel door, metal door, steel, sheet metal); rebar-mesh (rebar grid, rebar).\n\nEvery figure is a published laboratory or in-building measurement, or a clearly labelled model, of one specimen at normal incidence; none is a prediction for your wall. Ranges span specimens that differ in thickness, construction (studs, cores, rebar, coatings), moisture, and test method, so a range is not a typical value or an uncertainty interval. Coverage is uneven: at 2.4 GHz only a few materials have a measured fit (ultra-wideband fits for drywall, plywood, wood doors, foam, brick, block, and cloth partitions) and the rest rely on calculated estimates, whose confidence tiers and distances say how much to trust them; several materials also have a single source. Oblique incidence, reflections, diffraction, and whole-building entry loss are not covered; for entry loss see ITU-R P.2109 and P.2346. Verify with a site survey before committing to a design.",
        "externalDocs": {
          "description": "RF attenuation through building materials in the browser, with the sources and method explained.",
          "url": "https://siterfapp.com/tools/rf-attenuation-building-materials/"
        },
        "parameters": [
          {
            "name": "material",
            "in": "query",
            "required": false,
            "description": "Material class id. Omit to get every material's coverage and, with `frequency`, its measured range. Aliases such as plasterboard or cinder block are listed under `materials[].aliases`; use the id.",
            "schema": {
              "type": "string",
              "enum": [
                "drywall",
                "ceiling-tile",
                "engineered-board",
                "plywood",
                "wood",
                "concrete",
                "reinforced-concrete",
                "concrete-block",
                "lightweight-concrete",
                "brick",
                "masonry-composite",
                "glass",
                "low-e-glass",
                "whiteboard",
                "foam",
                "cloth-partition",
                "metal",
                "rebar-mesh"
              ]
            },
            "example": "drywall"
          },
          {
            "name": "frequency",
            "in": "query",
            "required": false,
            "description": "Frequency to look up, in `frequency_unit`. Between 100 MHz and 500 GHz. Omit to see what each record spans.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "example": 6.5
          },
          {
            "name": "frequency_unit",
            "in": "query",
            "required": false,
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "schema": {
              "type": "string",
              "enum": [
                "MHz",
                "GHz"
              ],
              "default": "GHz"
            },
            "example": "GHz"
          },
          {
            "name": "estimates",
            "in": "query",
            "required": false,
            "description": "Calculated estimates for specimens with no measurement at the frequency, kept apart from measurements. `all` (default): beside any measurements, so a thick wall is not hidden by a thin panel's measurement. `auto`: only when no measurement at all covers the frequency. `none`: measurements only. Needs `frequency`; detail is returned when `material` is given. Defaults to all.",
            "schema": {
              "type": "string",
              "enum": [
                "auto",
                "all",
                "none"
              ],
              "default": "all"
            },
            "example": "all"
          },
          {
            "name": "max_distance_pct",
            "in": "query",
            "required": false,
            "description": "How far from a measurement an estimate may be, as a percentage of the frequency: 50 accepts anchors between 0.67x and 1.5x of it. Raise it for more (lower-confidence) estimates, lower it to be strict; 0 returns none. Every estimate reports its own distance and confidence. Unit: %. Defaults to 50.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 400,
              "default": 50
            },
            "example": 50
          },
          {
            "name": "thickness_mm",
            "in": "query",
            "required": false,
            "description": "Your wall's thickness, used only for the ITU-R P.2040 slab model estimate. Measured records keep the thickness they were measured at. Requires `frequency`. Unit: mm.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0,
              "maximum": 2000
            },
            "example": 13
          },
          {
            "name": "hints",
            "in": "query",
            "required": false,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The lookup succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingMaterialAttenuationResponse"
                },
                "example": {
                  "tool": "building-material-attenuation",
                  "inputs": {
                    "material": "drywall",
                    "frequency": 6.5,
                    "frequency_unit": "GHz",
                    "estimates": "all",
                    "max_distance_pct": 50,
                    "thickness_mm": 13
                  },
                  "result": {
                    "query": {
                      "material": "drywall",
                      "frequency_ghz": 6.5,
                      "thickness_mm": 13,
                      "estimates": "all",
                      "max_distance_pct": 50
                    },
                    "dataset": {
                      "version": "2026-10-20",
                      "record_count": 171,
                      "source_count": 9,
                      "material_count": 18
                    },
                    "how_to_read": [
                      "Loss ranges are the spread across published specimens that differ in thickness, construction, moisture and test method. They are not a typical value, an average, or an uncertainty interval.",
                      "A panel is one sheet of material. An assembly is a built wall, door, window or stack of units, with whatever frame, studs, cores, or coatings it has. Do not substitute one for the other.",
                      "A fit is only reported inside the frequencies it was measured at. Records just outside are listed separately, at the frequency they were measured, and are never extrapolated.",
                      "Models (a standard's formula, or a physics calculation) are estimates, listed apart from measurements. Check the nearest measured record before using one."
                    ],
                    "materials": [
                      {
                        "id": "drywall",
                        "label": "Drywall / gypsum board",
                        "family": "boards",
                        "aliases": [
                          "plasterboard",
                          "gypsum board",
                          "gypsum wallboard",
                          "sheetrock",
                          "wallboard"
                        ],
                        "description": "Gypsum board panels, alone or fixed to a stud or metal-frame partition.",
                        "catalog": {
                          "record_count": 15,
                          "source_count": 6,
                          "measured_ghz": [
                            [
                              0.5,
                              40
                            ],
                            [
                              73,
                              73
                            ],
                            [
                              91,
                              91
                            ]
                          ],
                          "thickness_mm": {
                            "min": 6,
                            "max": 137
                          }
                        },
                        "measured_in_band": {
                          "record_count": 9,
                          "source_count": 4,
                          "loss_db": {
                            "min": 0,
                            "max": 3
                          },
                          "thickness_mm": {
                            "min": 6,
                            "max": 137
                          },
                          "includes_wet_specimens": false,
                          "by_specimen_kind": {
                            "panel": {
                              "record_count": 7,
                              "loss_db": {
                                "min": 0,
                                "max": 1.5
                              },
                              "thickness_mm": {
                                "min": 6,
                                "max": 30
                              }
                            },
                            "assembly": {
                              "record_count": 2,
                              "loss_db": {
                                "min": 2.1,
                                "max": 3
                              },
                              "thickness_mm": {
                                "min": 137,
                                "max": 137
                              }
                            }
                          }
                        },
                        "coverage_note": "9 measured records from 4 sources cover 6.5 GHz.",
                        "estimate_summary": null,
                        "records": [
                          {
                            "id": "nistir-6055/4.13d/c1",
                            "source": "nistir-6055",
                            "locator": "Table 4.13d, report p. 139",
                            "specimen": "Drywall panel, 6 mm nominal",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 6,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                3,
                                8
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.1,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": []
                          },
                          {
                            "id": "muqaibel-2003/wallboard",
                            "source": "muqaibel-2003",
                            "locator": "Table 4.3 (Wallboard)",
                            "specimen": "Wallboard sheet (relative permittivity 2.44)",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 11.7,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                0.6,
                                13.9
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.5,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": [
                              "A straight-line fit to the ultra-wideband insertion loss over the stated range; individual frequencies ripple around it. Table 4.3 gives 0.45 dB at 5 GHz.",
                              "Listed as 'wallboard' in the source; grouped with drywall here."
                            ]
                          },
                          {
                            "id": "wallace-2018/drywall",
                            "source": "wallace-2018",
                            "locator": "Table I (DRYWL)",
                            "specimen": "Drywall sheet",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 12,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                4,
                                40
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.2,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": [
                              "Straight-line fit over 4-40 GHz; the authors describe drywall as showing periodic behaviour around the fit."
                            ]
                          },
                          {
                            "id": "nistir-6055/4.13d/c2",
                            "source": "nistir-6055",
                            "locator": "Table 4.13d, report p. 139",
                            "specimen": "Drywall panel, 13 mm nominal",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 13,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                3,
                                8
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": []
                          },
                          {
                            "id": "nistir-6055/4.13d/c3",
                            "source": "nistir-6055",
                            "locator": "Table 4.13d, report p. 139",
                            "specimen": "Drywall panel, 16 mm nominal",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 16,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                3,
                                8
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.5,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": []
                          },
                          {
                            "id": "shakya-2024/drywall-panel-co",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "USG Sheetrock gypsum drywall panel, 4 x 8 ft",
                            "kind": "panel",
                            "evidence": "measurement",
                            "thickness_mm": 30,
                            "condition": null,
                            "polarization": "co",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 0.6,
                            "loss_range_db": null,
                            "sigma_db": 0.1,
                            "notes": [
                              "Thickness as printed in the paper."
                            ]
                          },
                          {
                            "id": "shakya-2024/drywall-panel-cross",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "USG Sheetrock gypsum drywall panel, 4 x 8 ft",
                            "kind": "panel",
                            "evidence": "measurement",
                            "thickness_mm": 30,
                            "condition": null,
                            "polarization": "cross",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 1.5,
                            "loss_range_db": null,
                            "sigma_db": 0.2,
                            "notes": [
                              "Thickness as printed in the paper."
                            ]
                          },
                          {
                            "id": "shakya-2024/plasterboard-wall-co",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "Plasterboard room partition on a metal frame (measured between studs)",
                            "kind": "assembly",
                            "evidence": "measurement",
                            "thickness_mm": 137,
                            "condition": null,
                            "polarization": "co",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 2.1,
                            "loss_range_db": null,
                            "sigma_db": 1,
                            "notes": [
                              "A stud detector was used to keep metal studs out of the measured area; a path through a stud would lose more.",
                              "Thickness is the whole partition."
                            ]
                          },
                          {
                            "id": "shakya-2024/plasterboard-wall-cross",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "Plasterboard room partition on a metal frame (measured between studs)",
                            "kind": "assembly",
                            "evidence": "measurement",
                            "thickness_mm": 137,
                            "condition": null,
                            "polarization": "cross",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 3,
                            "loss_range_db": null,
                            "sigma_db": 0.6,
                            "notes": [
                              "A stud detector was used to keep metal studs out of the measured area; a path through a stud would lose more.",
                              "Thickness is the whole partition."
                            ]
                          }
                        ],
                        "nearby_records": [],
                        "estimates": [],
                        "model_estimates": [
                          {
                            "id": "itu-r-p2040/drywall",
                            "source": "itu-r-p2040",
                            "model": "ITU-R P.2040-4 homogeneous slab (Plasterboard)",
                            "evidence": "physics_model",
                            "specimen": "Homogeneous plasterboard slab at normal incidence",
                            "thickness_mm": 13,
                            "thickness_basis": "requested",
                            "frequency_ghz": 6.5,
                            "loss_db": 0.7,
                            "within_stated_range": true,
                            "notes": [
                              "Computed for the 13 mm you gave.",
                              "Ignores studs, rebar, voids, coatings, moisture and oblique incidence, so real assemblies usually lose more."
                            ]
                          }
                        ]
                      }
                    ],
                    "sources": [
                      {
                        "id": "nistir-6055",
                        "citation": "William C. Stone (1997). Electromagnetic Signal Attenuation in Construction Materials (NISTIR 6055). National Institute of Standards and Technology, NIST Construction Automation Program Report No. 3.",
                        "url": "https://doi.org/10.6028/NIST.IR.6055",
                        "doi": null,
                        "evidence": "measured_fit",
                        "frequency_range_ghz": [
                          0.5,
                          8
                        ],
                        "method": "Laboratory transmission through specimens between horn antennas 2 m apart with a metal multipath shield; spread-spectrum swept CW at 2 MHz steps, time-gated, averaged over ten positions. Reported as polynomial fits of received signal relative to free space.",
                        "license": "United States Government work (NIST); not subject to copyright in the United States.",
                        "known_gaps": "Two bands only: 0.5-2.0 GHz and 3.0-8.0 GHz, so 2.4 GHz falls between them. The instrument's stated dynamic range is 80 dB.",
                        "sha256": "e5923af29e632861b312b0b4b9c469c3620e669928cdd4733e0e108955b31f70"
                      },
                      {
                        "id": "wallace-2018",
                        "citation": "Jon W. Wallace, Rashid Mehmood, Michael A. Jensen (2018). 4-40 GHz Transmission Measurement of Indoor Building Materials at Normal Incidence. 2018 IEEE International Symposium on Antennas and Propagation (AP-S), pp. 2483-2484.",
                        "url": "https://par.nsf.gov/biblio/10112166",
                        "doi": "10.1109/APUSNCURSINRSM.2018.8609395",
                        "evidence": "measured_fit",
                        "frequency_range_ghz": [
                          4,
                          40
                        ],
                        "method": "Normal-incidence transmission in a compact anechoic chamber, double-ridged horns 50 cm apart, vector network analyzer. Reported as least-squares straight-line fits of transmission (dB) against frequency (GHz).",
                        "license": "Not stated. Public-access accepted manuscript on the NSF Public Access Repository; values transcribed with attribution.",
                        "known_gaps": "Straight-line fits over 4-40 GHz smooth over the periodic behaviour the authors describe for glass, wood and drywall. Samples are small (at least 40 cm x 40 cm) panels, not installed walls.",
                        "sha256": "635f88cc63787baf119fdd174aa7d9f84aaa8801a249a52d3074acb8095a3827"
                      },
                      {
                        "id": "shakya-2024",
                        "citation": "Dipankar Shakya, Mingjun Ying, Theodore S. Rappaport, Hitesh Poddar, Peijie Ma, Yanbo Wang, Idris Al-Wazani (2024). Wideband Penetration Loss through Building Materials and Partitions at 6.75 GHz in FR1(C) and 16.95 GHz in the FR3 Upper Mid-band spectrum. IEEE GLOBECOM 2024 (arXiv:2405.01362).",
                        "url": "https://arxiv.org/abs/2405.01362",
                        "doi": "10.48550/arXiv.2405.01362",
                        "evidence": "measurement",
                        "frequency_range_ghz": [
                          6.75,
                          16.95
                        ],
                        "method": "1 GHz-wideband sliding-correlation channel sounder, horn antennas on boresight (normal incidence), first-arriving multipath only. Co- and cross-polarised. Panels measured in a lab; walls and doors measured in place in a university building.",
                        "license": "CC BY 4.0",
                        "known_gaps": "Two frequencies. The paper's text and Table II disagree on the clear-glass thickness (0.5 cm in the text, 1 cm in the table).",
                        "sha256": "a257e03cddd92a72fd5698ed885ee839333ff3e2f57e2bbcccfbca42f53fc10a"
                      },
                      {
                        "id": "muqaibel-2003",
                        "citation": "Ali Hussein Muqaibel (2003). Characterization of Ultra Wideband Communication Channels (Ph.D. dissertation, Virginia Tech). Virginia Polytechnic Institute and State University, Bradley Department of Electrical and Computer Engineering.",
                        "url": "https://vtechworks.lib.vt.edu/handle/10919/26398",
                        "doi": null,
                        "evidence": "measured_fit",
                        "frequency_range_ghz": [
                          0.5,
                          14.6
                        ],
                        "method": "Ultra-wideband free-space transmission measurements of installed-style samples and walls built on a moving platform. Table 4.3 gives each sample's insertion loss at 5 GHz and a straight-line fit over its measured frequency range; a fit is used here only where it reproduces the 5 GHz value within 0.15 dB, otherwise the 5 GHz point is used.",
                        "license": "Not stated on the repository record; values transcribed with attribution.",
                        "known_gaps": "Straight-line fits smooth over the ripple of thin panels. Two rows are entered as 5 GHz points only because their printed fit is unusable (glass, structural wood). Brick and concrete block are measurable only over 1-7 and 2-6.8 GHz because of their loss.",
                        "sha256": "e2153ee215f6b12624102c32ddb8f50371fb6fb87a609b074a37fbd3d6888c21"
                      },
                      {
                        "id": "itu-r-p2040",
                        "citation": "International Telecommunication Union, Radiocommunication Sector (2025). Recommendation ITU-R P.2040-4 (09/2025): Effects of building materials and structures on radiowave propagation in the range of 1 MHz to 450 GHz. ITU-R Recommendation.",
                        "url": "https://www.itu.int/rec/R-REC-P.2040-4-202509-I/en",
                        "doi": null,
                        "evidence": "physics_model",
                        "frequency_range_ghz": [
                          1,
                          100
                        ],
                        "method": "Table 3 gives relative permittivity a*f^b and conductivity c*f^d per material class. SiteRF computes the normal-incidence power transmission of one homogeneous slab of the requested thickness, including both surface reflections and internal multiple reflections.",
                        "license": "ITU-R Recommendations are freely downloadable; Table 3 parameters are cited, not reproduced at length.",
                        "known_gaps": "A homogeneous slab: no studs, rebar, voids, coatings, moisture gradient, or oblique incidence. Computed at the thickness you give, so it is only as good as that thickness and the material class.",
                        "sha256": "f2b73e144129f52ff2c0bf2789dcf86f573035caffbc8e1549e691c9cfc023e3"
                      }
                    ]
                  },
                  "assumptions": "Every figure is a published laboratory or in-building measurement, or a clearly labelled model, of one specimen at normal incidence; none is a prediction for your wall. Ranges span specimens that differ in thickness, construction (studs, cores, rebar, coatings), moisture, and test method, so a range is not a typical value or an uncertainty interval. Coverage is uneven: at 2.4 GHz only a few materials have a measured fit (ultra-wideband fits for drywall, plywood, wood doors, foam, brick, block, and cloth partitions) and the rest rely on calculated estimates, whose confidence tiers and distances say how much to trust them; several materials also have a single source. Oblique incidence, reflections, diffraction, and whole-building entry loss are not covered; for entry loss see ITU-R P.2109 and P.2346. Verify with a site survey before committing to a design.",
                  "reference": "SiteRF building-material attenuation dataset 2026-10-20: 9 sources, 171 records. Method and sources: https://siterfapp.com/tools/rf-attenuation-building-materials/#sources",
                  "next_steps": [
                    {
                      "id": "rf_solve_coverage",
                      "why": "These are published specimens, not your wall. Applying a loss to the walls of a floor plan and simulating coverage needs a floor plan.",
                      "requires": "account_with_approval",
                      "status": "available",
                      "learn_more": "https://siterfapp.com/agents/#rf_solve_coverage"
                    }
                  ],
                  "source": {
                    "name": "SiteRF RF attenuation through building materials reference",
                    "url": "https://siterfapp.com/tools/rf-attenuation-building-materials/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "buildingMaterialAttenuationPost",
        "tags": [
          "ReferenceData"
        ],
        "summary": "Published RF attenuation of building materials by frequency, with sources and limitations (JSON body)",
        "description": "Looks up published measurements of how much radio signal common building materials absorb and reflect, for Wi-Fi, CBRS, and indoor cellular planning. It returns the spread across dated, cited specimens, split into single panels and built assemblies, together with the frequencies, thicknesses, test conditions, and caveats of each. It deliberately does not return one loss value per material: drywall, brick, concrete, and glass each vary by an order of magnitude with thickness, construction, moisture, and frequency. Standard-formula and physics-model estimates are listed apart from measurements. Fits are never extrapolated beyond the frequencies they were measured at. Where nothing measured covers the frequency (2.4 GHz is the common case), it calculates labelled estimates instead, by interpolating between a specimen's own measurements on either side or by scaling a nearby measurement with a physics model; each carries a confidence tier and its distance from a measurement, and `max_distance_pct` lets the caller decide how far from a measurement to accept. Material ids: drywall (plasterboard, gypsum board, gypsum wallboard, sheetrock, wallboard); ceiling-tile (acoustic ceiling tile, gypsum ceiling tile, ceiling board); engineered-board (chipboard, particle board, particleboard, pressed wood board); plywood (birch plywood); wood (lumber, timber, wooden door, solid wood, wood door); concrete (plain concrete, cement); reinforced-concrete (rebar concrete, steel-reinforced concrete); concrete-block (cinder block, cinderblock, cmu, masonry block, breeze block); lightweight-concrete (aac, aerated concrete, autoclaved aerated concrete, leca, lightweight concrete); brick (brickwork, brick wall); masonry-composite (brick veneer, brick-faced concrete, brick-faced block); glass (clear glass, window glass, float glass, double-glazed glass); low-e-glass (low-e glass, irr glass, infrared reflective glass, tinted glass, energy-efficient glass, coated glass); whiteboard; foam (styrofoam, eva foam, foam board, polystyrene, eps, wood fibre insulation, insulation board); cloth-partition (cubicle partition, fabric partition); metal (steel door, metal door, steel, sheet metal); rebar-mesh (rebar grid, rebar).\n\nSame lookup as the GET operation, with parameters in a JSON body. Numbers must be JSON numbers.",
        "externalDocs": {
          "description": "RF attenuation through building materials in the browser, with the sources and method explained.",
          "url": "https://siterfapp.com/tools/rf-attenuation-building-materials/"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuildingMaterialAttenuationRequest"
              },
              "example": {
                "material": "drywall",
                "frequency": 6.5,
                "frequency_unit": "GHz",
                "estimates": "all",
                "max_distance_pct": 50,
                "thickness_mm": 13
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The lookup succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingMaterialAttenuationResponse"
                },
                "example": {
                  "tool": "building-material-attenuation",
                  "inputs": {
                    "material": "drywall",
                    "frequency": 6.5,
                    "frequency_unit": "GHz",
                    "estimates": "all",
                    "max_distance_pct": 50,
                    "thickness_mm": 13
                  },
                  "result": {
                    "query": {
                      "material": "drywall",
                      "frequency_ghz": 6.5,
                      "thickness_mm": 13,
                      "estimates": "all",
                      "max_distance_pct": 50
                    },
                    "dataset": {
                      "version": "2026-10-20",
                      "record_count": 171,
                      "source_count": 9,
                      "material_count": 18
                    },
                    "how_to_read": [
                      "Loss ranges are the spread across published specimens that differ in thickness, construction, moisture and test method. They are not a typical value, an average, or an uncertainty interval.",
                      "A panel is one sheet of material. An assembly is a built wall, door, window or stack of units, with whatever frame, studs, cores, or coatings it has. Do not substitute one for the other.",
                      "A fit is only reported inside the frequencies it was measured at. Records just outside are listed separately, at the frequency they were measured, and are never extrapolated.",
                      "Models (a standard's formula, or a physics calculation) are estimates, listed apart from measurements. Check the nearest measured record before using one."
                    ],
                    "materials": [
                      {
                        "id": "drywall",
                        "label": "Drywall / gypsum board",
                        "family": "boards",
                        "aliases": [
                          "plasterboard",
                          "gypsum board",
                          "gypsum wallboard",
                          "sheetrock",
                          "wallboard"
                        ],
                        "description": "Gypsum board panels, alone or fixed to a stud or metal-frame partition.",
                        "catalog": {
                          "record_count": 15,
                          "source_count": 6,
                          "measured_ghz": [
                            [
                              0.5,
                              40
                            ],
                            [
                              73,
                              73
                            ],
                            [
                              91,
                              91
                            ]
                          ],
                          "thickness_mm": {
                            "min": 6,
                            "max": 137
                          }
                        },
                        "measured_in_band": {
                          "record_count": 9,
                          "source_count": 4,
                          "loss_db": {
                            "min": 0,
                            "max": 3
                          },
                          "thickness_mm": {
                            "min": 6,
                            "max": 137
                          },
                          "includes_wet_specimens": false,
                          "by_specimen_kind": {
                            "panel": {
                              "record_count": 7,
                              "loss_db": {
                                "min": 0,
                                "max": 1.5
                              },
                              "thickness_mm": {
                                "min": 6,
                                "max": 30
                              }
                            },
                            "assembly": {
                              "record_count": 2,
                              "loss_db": {
                                "min": 2.1,
                                "max": 3
                              },
                              "thickness_mm": {
                                "min": 137,
                                "max": 137
                              }
                            }
                          }
                        },
                        "coverage_note": "9 measured records from 4 sources cover 6.5 GHz.",
                        "estimate_summary": null,
                        "records": [
                          {
                            "id": "nistir-6055/4.13d/c1",
                            "source": "nistir-6055",
                            "locator": "Table 4.13d, report p. 139",
                            "specimen": "Drywall panel, 6 mm nominal",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 6,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                3,
                                8
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.1,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": []
                          },
                          {
                            "id": "muqaibel-2003/wallboard",
                            "source": "muqaibel-2003",
                            "locator": "Table 4.3 (Wallboard)",
                            "specimen": "Wallboard sheet (relative permittivity 2.44)",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 11.7,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                0.6,
                                13.9
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.5,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": [
                              "A straight-line fit to the ultra-wideband insertion loss over the stated range; individual frequencies ripple around it. Table 4.3 gives 0.45 dB at 5 GHz.",
                              "Listed as 'wallboard' in the source; grouped with drywall here."
                            ]
                          },
                          {
                            "id": "wallace-2018/drywall",
                            "source": "wallace-2018",
                            "locator": "Table I (DRYWL)",
                            "specimen": "Drywall sheet",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 12,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                4,
                                40
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.2,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": [
                              "Straight-line fit over 4-40 GHz; the authors describe drywall as showing periodic behaviour around the fit."
                            ]
                          },
                          {
                            "id": "nistir-6055/4.13d/c2",
                            "source": "nistir-6055",
                            "locator": "Table 4.13d, report p. 139",
                            "specimen": "Drywall panel, 13 mm nominal",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 13,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                3,
                                8
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": []
                          },
                          {
                            "id": "nistir-6055/4.13d/c3",
                            "source": "nistir-6055",
                            "locator": "Table 4.13d, report p. 139",
                            "specimen": "Drywall panel, 16 mm nominal",
                            "kind": "panel",
                            "evidence": "measured_fit",
                            "thickness_mm": 16,
                            "condition": null,
                            "polarization": null,
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.5,
                              "match": "in_band",
                              "measured_span_ghz": [
                                3,
                                8
                              ],
                              "measured_points_ghz": null
                            },
                            "loss_db": 0.5,
                            "loss_range_db": null,
                            "sigma_db": null,
                            "notes": []
                          },
                          {
                            "id": "shakya-2024/drywall-panel-co",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "USG Sheetrock gypsum drywall panel, 4 x 8 ft",
                            "kind": "panel",
                            "evidence": "measurement",
                            "thickness_mm": 30,
                            "condition": null,
                            "polarization": "co",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 0.6,
                            "loss_range_db": null,
                            "sigma_db": 0.1,
                            "notes": [
                              "Thickness as printed in the paper."
                            ]
                          },
                          {
                            "id": "shakya-2024/drywall-panel-cross",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "USG Sheetrock gypsum drywall panel, 4 x 8 ft",
                            "kind": "panel",
                            "evidence": "measurement",
                            "thickness_mm": 30,
                            "condition": null,
                            "polarization": "cross",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 1.5,
                            "loss_range_db": null,
                            "sigma_db": 0.2,
                            "notes": [
                              "Thickness as printed in the paper."
                            ]
                          },
                          {
                            "id": "shakya-2024/plasterboard-wall-co",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "Plasterboard room partition on a metal frame (measured between studs)",
                            "kind": "assembly",
                            "evidence": "measurement",
                            "thickness_mm": 137,
                            "condition": null,
                            "polarization": "co",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 2.1,
                            "loss_range_db": null,
                            "sigma_db": 1,
                            "notes": [
                              "A stud detector was used to keep metal studs out of the measured area; a path through a stud would lose more.",
                              "Thickness is the whole partition."
                            ]
                          },
                          {
                            "id": "shakya-2024/plasterboard-wall-cross",
                            "source": "shakya-2024",
                            "locator": "Table II",
                            "specimen": "Plasterboard room partition on a metal frame (measured between studs)",
                            "kind": "assembly",
                            "evidence": "measurement",
                            "thickness_mm": 137,
                            "condition": null,
                            "polarization": "cross",
                            "frequency": {
                              "requested_ghz": 6.5,
                              "evaluated_at_ghz": 6.75,
                              "match": "in_band",
                              "measured_span_ghz": null,
                              "measured_points_ghz": [
                                6.75,
                                16.95
                              ]
                            },
                            "loss_db": 3,
                            "loss_range_db": null,
                            "sigma_db": 0.6,
                            "notes": [
                              "A stud detector was used to keep metal studs out of the measured area; a path through a stud would lose more.",
                              "Thickness is the whole partition."
                            ]
                          }
                        ],
                        "nearby_records": [],
                        "estimates": [],
                        "model_estimates": [
                          {
                            "id": "itu-r-p2040/drywall",
                            "source": "itu-r-p2040",
                            "model": "ITU-R P.2040-4 homogeneous slab (Plasterboard)",
                            "evidence": "physics_model",
                            "specimen": "Homogeneous plasterboard slab at normal incidence",
                            "thickness_mm": 13,
                            "thickness_basis": "requested",
                            "frequency_ghz": 6.5,
                            "loss_db": 0.7,
                            "within_stated_range": true,
                            "notes": [
                              "Computed for the 13 mm you gave.",
                              "Ignores studs, rebar, voids, coatings, moisture and oblique incidence, so real assemblies usually lose more."
                            ]
                          }
                        ]
                      }
                    ],
                    "sources": [
                      {
                        "id": "nistir-6055",
                        "citation": "William C. Stone (1997). Electromagnetic Signal Attenuation in Construction Materials (NISTIR 6055). National Institute of Standards and Technology, NIST Construction Automation Program Report No. 3.",
                        "url": "https://doi.org/10.6028/NIST.IR.6055",
                        "doi": null,
                        "evidence": "measured_fit",
                        "frequency_range_ghz": [
                          0.5,
                          8
                        ],
                        "method": "Laboratory transmission through specimens between horn antennas 2 m apart with a metal multipath shield; spread-spectrum swept CW at 2 MHz steps, time-gated, averaged over ten positions. Reported as polynomial fits of received signal relative to free space.",
                        "license": "United States Government work (NIST); not subject to copyright in the United States.",
                        "known_gaps": "Two bands only: 0.5-2.0 GHz and 3.0-8.0 GHz, so 2.4 GHz falls between them. The instrument's stated dynamic range is 80 dB.",
                        "sha256": "e5923af29e632861b312b0b4b9c469c3620e669928cdd4733e0e108955b31f70"
                      },
                      {
                        "id": "wallace-2018",
                        "citation": "Jon W. Wallace, Rashid Mehmood, Michael A. Jensen (2018). 4-40 GHz Transmission Measurement of Indoor Building Materials at Normal Incidence. 2018 IEEE International Symposium on Antennas and Propagation (AP-S), pp. 2483-2484.",
                        "url": "https://par.nsf.gov/biblio/10112166",
                        "doi": "10.1109/APUSNCURSINRSM.2018.8609395",
                        "evidence": "measured_fit",
                        "frequency_range_ghz": [
                          4,
                          40
                        ],
                        "method": "Normal-incidence transmission in a compact anechoic chamber, double-ridged horns 50 cm apart, vector network analyzer. Reported as least-squares straight-line fits of transmission (dB) against frequency (GHz).",
                        "license": "Not stated. Public-access accepted manuscript on the NSF Public Access Repository; values transcribed with attribution.",
                        "known_gaps": "Straight-line fits over 4-40 GHz smooth over the periodic behaviour the authors describe for glass, wood and drywall. Samples are small (at least 40 cm x 40 cm) panels, not installed walls.",
                        "sha256": "635f88cc63787baf119fdd174aa7d9f84aaa8801a249a52d3074acb8095a3827"
                      },
                      {
                        "id": "shakya-2024",
                        "citation": "Dipankar Shakya, Mingjun Ying, Theodore S. Rappaport, Hitesh Poddar, Peijie Ma, Yanbo Wang, Idris Al-Wazani (2024). Wideband Penetration Loss through Building Materials and Partitions at 6.75 GHz in FR1(C) and 16.95 GHz in the FR3 Upper Mid-band spectrum. IEEE GLOBECOM 2024 (arXiv:2405.01362).",
                        "url": "https://arxiv.org/abs/2405.01362",
                        "doi": "10.48550/arXiv.2405.01362",
                        "evidence": "measurement",
                        "frequency_range_ghz": [
                          6.75,
                          16.95
                        ],
                        "method": "1 GHz-wideband sliding-correlation channel sounder, horn antennas on boresight (normal incidence), first-arriving multipath only. Co- and cross-polarised. Panels measured in a lab; walls and doors measured in place in a university building.",
                        "license": "CC BY 4.0",
                        "known_gaps": "Two frequencies. The paper's text and Table II disagree on the clear-glass thickness (0.5 cm in the text, 1 cm in the table).",
                        "sha256": "a257e03cddd92a72fd5698ed885ee839333ff3e2f57e2bbcccfbca42f53fc10a"
                      },
                      {
                        "id": "muqaibel-2003",
                        "citation": "Ali Hussein Muqaibel (2003). Characterization of Ultra Wideband Communication Channels (Ph.D. dissertation, Virginia Tech). Virginia Polytechnic Institute and State University, Bradley Department of Electrical and Computer Engineering.",
                        "url": "https://vtechworks.lib.vt.edu/handle/10919/26398",
                        "doi": null,
                        "evidence": "measured_fit",
                        "frequency_range_ghz": [
                          0.5,
                          14.6
                        ],
                        "method": "Ultra-wideband free-space transmission measurements of installed-style samples and walls built on a moving platform. Table 4.3 gives each sample's insertion loss at 5 GHz and a straight-line fit over its measured frequency range; a fit is used here only where it reproduces the 5 GHz value within 0.15 dB, otherwise the 5 GHz point is used.",
                        "license": "Not stated on the repository record; values transcribed with attribution.",
                        "known_gaps": "Straight-line fits smooth over the ripple of thin panels. Two rows are entered as 5 GHz points only because their printed fit is unusable (glass, structural wood). Brick and concrete block are measurable only over 1-7 and 2-6.8 GHz because of their loss.",
                        "sha256": "e2153ee215f6b12624102c32ddb8f50371fb6fb87a609b074a37fbd3d6888c21"
                      },
                      {
                        "id": "itu-r-p2040",
                        "citation": "International Telecommunication Union, Radiocommunication Sector (2025). Recommendation ITU-R P.2040-4 (09/2025): Effects of building materials and structures on radiowave propagation in the range of 1 MHz to 450 GHz. ITU-R Recommendation.",
                        "url": "https://www.itu.int/rec/R-REC-P.2040-4-202509-I/en",
                        "doi": null,
                        "evidence": "physics_model",
                        "frequency_range_ghz": [
                          1,
                          100
                        ],
                        "method": "Table 3 gives relative permittivity a*f^b and conductivity c*f^d per material class. SiteRF computes the normal-incidence power transmission of one homogeneous slab of the requested thickness, including both surface reflections and internal multiple reflections.",
                        "license": "ITU-R Recommendations are freely downloadable; Table 3 parameters are cited, not reproduced at length.",
                        "known_gaps": "A homogeneous slab: no studs, rebar, voids, coatings, moisture gradient, or oblique incidence. Computed at the thickness you give, so it is only as good as that thickness and the material class.",
                        "sha256": "f2b73e144129f52ff2c0bf2789dcf86f573035caffbc8e1549e691c9cfc023e3"
                      }
                    ]
                  },
                  "assumptions": "Every figure is a published laboratory or in-building measurement, or a clearly labelled model, of one specimen at normal incidence; none is a prediction for your wall. Ranges span specimens that differ in thickness, construction (studs, cores, rebar, coatings), moisture, and test method, so a range is not a typical value or an uncertainty interval. Coverage is uneven: at 2.4 GHz only a few materials have a measured fit (ultra-wideband fits for drywall, plywood, wood doors, foam, brick, block, and cloth partitions) and the rest rely on calculated estimates, whose confidence tiers and distances say how much to trust them; several materials also have a single source. Oblique incidence, reflections, diffraction, and whole-building entry loss are not covered; for entry loss see ITU-R P.2109 and P.2346. Verify with a site survey before committing to a design.",
                  "reference": "SiteRF building-material attenuation dataset 2026-10-20: 9 sources, 171 records. Method and sources: https://siterfapp.com/tools/rf-attenuation-building-materials/#sources",
                  "next_steps": [
                    {
                      "id": "rf_solve_coverage",
                      "why": "These are published specimens, not your wall. Applying a loss to the walls of a floor plan and simulating coverage needs a floor plan.",
                      "requires": "account_with_approval",
                      "status": "available",
                      "learn_more": "https://siterfapp.com/agents/#rf_solve_coverage"
                    }
                  ],
                  "source": {
                    "name": "SiteRF RF attenuation through building materials reference",
                    "url": "https://siterfapp.com/tools/rf-attenuation-building-materials/",
                    "api_docs": "https://siterfapp.com/api/"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing, unknown, or out of range. `error.issues` lists each problem.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_input",
                    "message": "The request has invalid parameters.",
                    "issues": [
                      {
                        "param": "distance",
                        "message": "distance must be greater than 0."
                      }
                    ]
                  }
                }
              }
            }
          },
          "422": {
            "description": "The inputs are valid but the result is too large or small to represent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "tags": [
          "Account"
        ],
        "summary": "What an account unlocks beyond the free tools, with the status of each capability",
        "description": "Machine-readable list of what connecting a SiteRF account would unlock, each entry with a status (`available`, `preview`, `planned`) and what it requires. Anything not `available` is not usable yet. Human-readable: https://siterfapp.com/agents/.",
        "responses": {
          "200": {
            "description": "The capabilities document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Source": {
        "type": "object",
        "required": [
          "name",
          "url",
          "api_docs"
        ],
        "description": "Where the calculation comes from. Cite this when you use a result.",
        "properties": {
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The browser calculator for the same computation."
          },
          "api_docs": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "NextStep": {
        "type": "object",
        "required": [
          "id",
          "why",
          "requires",
          "status",
          "learn_more"
        ],
        "description": "A fact about a gap in this result, and where to read more. Not an instruction. `status` says whether the capability exists yet: only `available` is usable.",
        "properties": {
          "id": {
            "type": "string",
            "description": "A capability id from the capabilities document."
          },
          "why": {
            "type": "string",
            "description": "The gap in this result."
          },
          "requires": {
            "type": "string",
            "enum": [
              "none",
              "account",
              "account_with_approval"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "available",
              "preview",
              "planned"
            ]
          },
          "learn_more": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code, such as `invalid_input`, `invalid_json`, `unknown_tool`, `payload_too_large`, or `out_of_range`."
              },
              "message": {
                "type": "string"
              },
              "issues": {
                "type": "array",
                "description": "One entry per invalid parameter.",
                "items": {
                  "type": "object",
                  "required": [
                    "message"
                  ],
                  "properties": {
                    "param": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "FreeSpacePathLossInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "distance",
          "frequency"
        ],
        "properties": {
          "distance": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Straight-line distance between transmitter and receiver. Must be greater than zero.",
            "examples": [
              10
            ]
          },
          "distance_unit": {
            "type": "string",
            "enum": [
              "m",
              "km",
              "ft",
              "mi"
            ],
            "default": "m",
            "description": "Unit of `distance`: metres, kilometres, feet, or miles. Defaults to m.",
            "examples": [
              "m"
            ]
          },
          "frequency": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Carrier frequency. Must be greater than zero.",
            "examples": [
              5.5
            ]
          },
          "frequency_unit": {
            "type": "string",
            "enum": [
              "MHz",
              "GHz"
            ],
            "default": "GHz",
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "examples": [
              "GHz"
            ]
          }
        }
      },
      "FreeSpacePathLossRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "distance",
          "frequency"
        ],
        "properties": {
          "distance": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Straight-line distance between transmitter and receiver. Must be greater than zero.",
            "examples": [
              10
            ]
          },
          "distance_unit": {
            "type": "string",
            "enum": [
              "m",
              "km",
              "ft",
              "mi"
            ],
            "default": "m",
            "description": "Unit of `distance`: metres, kilometres, feet, or miles. Defaults to m.",
            "examples": [
              "m"
            ]
          },
          "frequency": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Carrier frequency. Must be greater than zero.",
            "examples": [
              5.5
            ]
          },
          "frequency_unit": {
            "type": "string",
            "enum": [
              "MHz",
              "GHz"
            ],
            "default": "GHz",
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "examples": [
              "GHz"
            ]
          },
          "hints": {
            "type": "boolean",
            "default": true,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`."
          }
        },
        "description": "Inputs for FreeSpacePathLoss. Same fields as the query string."
      },
      "FreeSpacePathLossResponse": {
        "type": "object",
        "required": [
          "tool",
          "inputs",
          "result",
          "formula",
          "assumptions",
          "source"
        ],
        "properties": {
          "tool": {
            "type": "string",
            "const": "free-space-path-loss"
          },
          "inputs": {
            "$ref": "#/components/schemas/FreeSpacePathLossInput",
            "description": "The inputs as used, with defaults applied."
          },
          "result": {
            "type": "object",
            "required": [
              "path_loss_db",
              "distance_m",
              "frequency_hz"
            ],
            "properties": {
              "path_loss_db": {
                "type": "number",
                "description": "Free-space path loss in dB."
              },
              "distance_m": {
                "type": "number",
                "description": "The distance converted to metres."
              },
              "frequency_hz": {
                "type": "number",
                "description": "The frequency converted to hertz."
              }
            }
          },
          "formula": {
            "type": "string",
            "description": "The formula behind the result."
          },
          "assumptions": {
            "type": "string",
            "description": "What the calculation assumes and leaves out. Read before relying on the result."
          },
          "reference": {
            "type": "string",
            "description": "The standard or source the calculation follows."
          },
          "next_steps": {
            "type": "array",
            "maxItems": 1,
            "items": {
              "$ref": "#/components/schemas/NextStep"
            },
            "description": "Present only when the result has a real gap and `hints` is not `false`."
          },
          "source": {
            "$ref": "#/components/schemas/Source"
          }
        }
      },
      "LinkBudgetInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transmit_power_dbm",
          "distance",
          "frequency"
        ],
        "properties": {
          "transmit_power_dbm": {
            "type": "number",
            "description": "Transmitter output power at the radio, in dBm. Unit: dBm.",
            "examples": [
              20
            ]
          },
          "transmit_gain_dbi": {
            "type": "number",
            "default": 0,
            "description": "Transmit antenna gain in dBi. Unit: dBi. Defaults to 0.",
            "examples": [
              4
            ]
          },
          "receive_gain_dbi": {
            "type": "number",
            "default": 0,
            "description": "Receive antenna gain in dBi. Unit: dBi. Defaults to 0.",
            "examples": [
              0
            ]
          },
          "cable_loss_db": {
            "type": "number",
            "minimum": 0,
            "default": 0,
            "description": "Transmit-side cable and connector loss in dB. Zero or more. Unit: dB. Defaults to 0.",
            "examples": [
              1
            ]
          },
          "other_loss_db": {
            "type": "number",
            "minimum": 0,
            "default": 0,
            "description": "Any other loss in dB (body, polarization, margin you want to subtract). Zero or more. Unit: dB. Defaults to 0.",
            "examples": [
              0
            ]
          },
          "distance": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Straight-line distance between transmitter and receiver. Must be greater than zero.",
            "examples": [
              10
            ]
          },
          "distance_unit": {
            "type": "string",
            "enum": [
              "m",
              "km",
              "ft",
              "mi"
            ],
            "default": "m",
            "description": "Unit of `distance`: metres, kilometres, feet, or miles. Defaults to m.",
            "examples": [
              "m"
            ]
          },
          "frequency": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Carrier frequency. Must be greater than zero.",
            "examples": [
              5.5
            ]
          },
          "frequency_unit": {
            "type": "string",
            "enum": [
              "MHz",
              "GHz"
            ],
            "default": "GHz",
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "examples": [
              "GHz"
            ]
          }
        }
      },
      "LinkBudgetRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "transmit_power_dbm",
          "distance",
          "frequency"
        ],
        "properties": {
          "transmit_power_dbm": {
            "type": "number",
            "description": "Transmitter output power at the radio, in dBm. Unit: dBm.",
            "examples": [
              20
            ]
          },
          "transmit_gain_dbi": {
            "type": "number",
            "default": 0,
            "description": "Transmit antenna gain in dBi. Unit: dBi. Defaults to 0.",
            "examples": [
              4
            ]
          },
          "receive_gain_dbi": {
            "type": "number",
            "default": 0,
            "description": "Receive antenna gain in dBi. Unit: dBi. Defaults to 0.",
            "examples": [
              0
            ]
          },
          "cable_loss_db": {
            "type": "number",
            "minimum": 0,
            "default": 0,
            "description": "Transmit-side cable and connector loss in dB. Zero or more. Unit: dB. Defaults to 0.",
            "examples": [
              1
            ]
          },
          "other_loss_db": {
            "type": "number",
            "minimum": 0,
            "default": 0,
            "description": "Any other loss in dB (body, polarization, margin you want to subtract). Zero or more. Unit: dB. Defaults to 0.",
            "examples": [
              0
            ]
          },
          "distance": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Straight-line distance between transmitter and receiver. Must be greater than zero.",
            "examples": [
              10
            ]
          },
          "distance_unit": {
            "type": "string",
            "enum": [
              "m",
              "km",
              "ft",
              "mi"
            ],
            "default": "m",
            "description": "Unit of `distance`: metres, kilometres, feet, or miles. Defaults to m.",
            "examples": [
              "m"
            ]
          },
          "frequency": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Carrier frequency. Must be greater than zero.",
            "examples": [
              5.5
            ]
          },
          "frequency_unit": {
            "type": "string",
            "enum": [
              "MHz",
              "GHz"
            ],
            "default": "GHz",
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "examples": [
              "GHz"
            ]
          },
          "hints": {
            "type": "boolean",
            "default": true,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`."
          }
        },
        "description": "Inputs for LinkBudget. Same fields as the query string."
      },
      "LinkBudgetResponse": {
        "type": "object",
        "required": [
          "tool",
          "inputs",
          "result",
          "formula",
          "assumptions",
          "source"
        ],
        "properties": {
          "tool": {
            "type": "string",
            "const": "link-budget"
          },
          "inputs": {
            "$ref": "#/components/schemas/LinkBudgetInput",
            "description": "The inputs as used, with defaults applied."
          },
          "result": {
            "type": "object",
            "required": [
              "eirp_dbm",
              "path_loss_db",
              "received_power_dbm"
            ],
            "properties": {
              "eirp_dbm": {
                "type": "number",
                "description": "Effective isotropic radiated power: transmit power plus transmit gain minus cable loss, in dBm."
              },
              "path_loss_db": {
                "type": "number",
                "description": "Free-space path loss over the distance, in dB."
              },
              "received_power_dbm": {
                "type": "number",
                "description": "Power at the receiver input, in dBm."
              }
            }
          },
          "formula": {
            "type": "string",
            "description": "The formula behind the result."
          },
          "assumptions": {
            "type": "string",
            "description": "What the calculation assumes and leaves out. Read before relying on the result."
          },
          "reference": {
            "type": "string",
            "description": "The standard or source the calculation follows."
          },
          "next_steps": {
            "type": "array",
            "maxItems": 1,
            "items": {
              "$ref": "#/components/schemas/NextStep"
            },
            "description": "Present only when the result has a real gap and `hints` is not `false`."
          },
          "source": {
            "$ref": "#/components/schemas/Source"
          }
        }
      },
      "DbmMwConversionInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value",
          "from"
        ],
        "properties": {
          "value": {
            "type": "number",
            "description": "The power to convert, in the unit given by `from`. Must be greater than zero when `from` is `mw`.",
            "examples": [
              -67
            ]
          },
          "from": {
            "type": "string",
            "enum": [
              "dbm",
              "mw"
            ],
            "description": "Unit of `value`: `dbm` or `mw` (milliwatts).",
            "examples": [
              "dbm"
            ]
          }
        }
      },
      "DbmMwConversionRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value",
          "from"
        ],
        "properties": {
          "value": {
            "type": "number",
            "description": "The power to convert, in the unit given by `from`. Must be greater than zero when `from` is `mw`.",
            "examples": [
              -67
            ]
          },
          "from": {
            "type": "string",
            "enum": [
              "dbm",
              "mw"
            ],
            "description": "Unit of `value`: `dbm` or `mw` (milliwatts).",
            "examples": [
              "dbm"
            ]
          },
          "hints": {
            "type": "boolean",
            "default": true,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`."
          }
        },
        "description": "Inputs for DbmMwConversion. Same fields as the query string."
      },
      "DbmMwConversionResponse": {
        "type": "object",
        "required": [
          "tool",
          "inputs",
          "result",
          "formula",
          "assumptions",
          "source"
        ],
        "properties": {
          "tool": {
            "type": "string",
            "const": "dbm-mw-conversion"
          },
          "inputs": {
            "$ref": "#/components/schemas/DbmMwConversionInput",
            "description": "The inputs as used, with defaults applied."
          },
          "result": {
            "type": "object",
            "required": [
              "dbm",
              "milliwatts",
              "watts"
            ],
            "properties": {
              "dbm": {
                "type": "number",
                "description": "The power in dBm."
              },
              "milliwatts": {
                "type": "number",
                "description": "The power in milliwatts."
              },
              "watts": {
                "type": "number",
                "description": "The power in watts."
              }
            }
          },
          "formula": {
            "type": "string",
            "description": "The formula behind the result."
          },
          "assumptions": {
            "type": "string",
            "description": "What the calculation assumes and leaves out. Read before relying on the result."
          },
          "next_steps": {
            "type": "array",
            "maxItems": 1,
            "items": {
              "$ref": "#/components/schemas/NextStep"
            },
            "description": "Present only when the result has a real gap and `hints` is not `false`."
          },
          "source": {
            "$ref": "#/components/schemas/Source"
          }
        }
      },
      "AttenuationLossRange": {
        "type": "object",
        "required": [
          "min",
          "max"
        ],
        "properties": {
          "min": {
            "type": "number"
          },
          "max": {
            "type": "number"
          }
        },
        "description": "Smallest and largest loss, in dB, across the records counted. Not an average and not an uncertainty interval."
      },
      "AttenuationRecord": {
        "type": "object",
        "description": "One published specimen, with where it came from and the frequency it applies to.",
        "required": [
          "id",
          "source",
          "locator",
          "specimen",
          "kind",
          "evidence",
          "frequency",
          "loss_db",
          "loss_range_db",
          "notes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable record id: `<source>/<table or key>`."
          },
          "source": {
            "type": "string",
            "description": "Source id; look it up in `result.sources`."
          },
          "locator": {
            "type": "string",
            "description": "Table, figure or section in the source the value comes from."
          },
          "specimen": {
            "type": "string",
            "description": "The specimen as the source describes it."
          },
          "kind": {
            "type": "string",
            "enum": [
              "panel",
              "assembly",
              "model"
            ],
            "description": "`panel`: one sheet or slab. `assembly`: a built wall, door, window, or stack of units, with its frame, studs, cores or coatings. `model`: not a specimen."
          },
          "evidence": {
            "type": "string",
            "enum": [
              "measurement",
              "measured_fit",
              "standard_model",
              "physics_model"
            ],
            "description": "`measurement`: values measured at specific frequencies. `measured_fit`: a curve fitted to measurements, reported only inside the measured frequencies."
          },
          "thickness_mm": {
            "type": [
              "number",
              "null"
            ]
          },
          "condition": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "dry",
              "wet",
              null
            ]
          },
          "polarization": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "co",
              "cross",
              null
            ],
            "description": "Antenna polarization for sources that report both; co- and cross-polarized losses can differ by 10 dB or more."
          },
          "frequency": {
            "type": "object",
            "required": [
              "requested_ghz",
              "evaluated_at_ghz",
              "match",
              "measured_span_ghz",
              "measured_points_ghz"
            ],
            "properties": {
              "requested_ghz": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "evaluated_at_ghz": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The frequency `loss_db` applies to. Equal to the request inside a measured span; otherwise the nearest measured edge or point."
              },
              "match": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "in_band",
                  "nearby",
                  null
                ],
                "description": "`in_band`: inside the measured span, or within 15% of a single-frequency measurement. `nearby`: up to 35% outside it; reported at the measured frequency, never extrapolated."
              },
              "measured_span_ghz": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "number"
                },
                "minItems": 2,
                "maxItems": 2
              },
              "measured_points_ghz": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "number"
                }
              }
            }
          },
          "loss_db": {
            "type": [
              "number",
              "null"
            ],
            "description": "Loss in dB at `evaluated_at_ghz`. Null when the source reports only a range, or when no frequency was requested."
          },
          "loss_range_db": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "[low, high] the record spans: the source's reported extremes, or the minimum and maximum over its measured span when no frequency was requested."
          },
          "sigma_db": {
            "type": [
              "number",
              "null"
            ],
            "description": "Standard deviation the source reports for the measurement, when it gives one."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Specimen-specific caveats from the source."
          }
        }
      },
      "AttenuationModelEstimate": {
        "type": "object",
        "description": "A model's estimate, listed apart from measurements.",
        "required": [
          "id",
          "source",
          "model",
          "evidence",
          "specimen",
          "thickness_mm",
          "thickness_basis",
          "frequency_ghz",
          "loss_db",
          "within_stated_range",
          "notes"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "evidence": {
            "type": "string",
            "enum": [
              "standard_model",
              "physics_model"
            ]
          },
          "specimen": {
            "type": "string"
          },
          "thickness_mm": {
            "type": [
              "number",
              "null"
            ]
          },
          "thickness_basis": {
            "type": "string",
            "enum": [
              "requested",
              "illustrative",
              "reference",
              "none"
            ],
            "description": "`requested`: the `thickness_mm` you gave. `illustrative`: a placeholder because you gave none. `reference`: the thickness the standard's formula assumes."
          },
          "frequency_ghz": {
            "type": "number"
          },
          "loss_db": {
            "type": "number"
          },
          "within_stated_range": {
            "type": "boolean",
            "description": "False when the frequency is outside the range of data the model was derived from."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AttenuationMaterial": {
        "type": "object",
        "required": [
          "id",
          "label",
          "family",
          "aliases",
          "description",
          "catalog",
          "measured_in_band",
          "coverage_note",
          "estimate_summary"
        ],
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "drywall",
              "ceiling-tile",
              "engineered-board",
              "plywood",
              "wood",
              "concrete",
              "reinforced-concrete",
              "concrete-block",
              "lightweight-concrete",
              "brick",
              "masonry-composite",
              "glass",
              "low-e-glass",
              "whiteboard",
              "foam",
              "cloth-partition",
              "metal",
              "rebar-mesh"
            ]
          },
          "label": {
            "type": "string"
          },
          "family": {
            "type": "string",
            "enum": [
              "boards",
              "wood",
              "masonry",
              "glass",
              "metal",
              "other"
            ]
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Other names this class goes by (plasterboard, cinder block...). Use the `id` in requests."
          },
          "description": {
            "type": "string"
          },
          "catalog": {
            "type": "object",
            "description": "Everything measured for this material, regardless of frequency.",
            "required": [
              "record_count",
              "source_count",
              "measured_ghz",
              "thickness_mm"
            ],
            "properties": {
              "record_count": {
                "type": "integer"
              },
              "source_count": {
                "type": "integer"
              },
              "measured_ghz": {
                "type": "array",
                "items": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  },
                  "minItems": 2,
                  "maxItems": 2
                },
                "description": "Merged [low, high] frequency intervals, in GHz, that at least one record covers. A single-frequency measurement is a zero-width interval."
              },
              "thickness_mm": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/AttenuationLossRange"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Smallest and largest specimen thickness among the records."
              }
            }
          },
          "measured_in_band": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "record_count",
                  "source_count",
                  "loss_db",
                  "thickness_mm",
                  "includes_wet_specimens",
                  "by_specimen_kind"
                ],
                "description": "The spread of measured loss at the requested frequency across published specimens. Read it with the thickness and the specimen kind; it is not a typical value.",
                "properties": {
                  "record_count": {
                    "type": "integer"
                  },
                  "source_count": {
                    "type": "integer"
                  },
                  "loss_db": {
                    "$ref": "#/components/schemas/AttenuationLossRange"
                  },
                  "thickness_mm": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/AttenuationLossRange"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "includes_wet_specimens": {
                    "type": "boolean",
                    "description": "True when soaked specimens are among the records; soaked specimens usually lost more than dry ones."
                  },
                  "by_specimen_kind": {
                    "type": "object",
                    "description": "The same spread split into single panels and built assemblies.",
                    "properties": {
                      "panel": {
                        "$ref": "#/components/schemas/AttenuationKindSummary"
                      },
                      "assembly": {
                        "$ref": "#/components/schemas/AttenuationKindSummary"
                      }
                    }
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Null when no frequency was requested or no measurement covers it."
          },
          "coverage_note": {
            "type": "string",
            "description": "Plain-language statement of what the data does and does not cover at the requested frequency."
          },
          "estimate_summary": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AttenuationEstimateSummary"
              },
              {
                "type": "null"
              }
            ],
            "description": "Present when calculated estimates exist for the frequency: their spread, and the same tier by tier. Kept apart from `measured_in_band`, which only ever contains measurements."
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttenuationRecord"
            },
            "description": "Present when `material` was requested: the measured records that cover the frequency (or all of them when no frequency was given)."
          },
          "nearby_records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttenuationRecord"
            },
            "description": "Present when `material` and `frequency` were requested: measurements just outside the frequency, at the frequency they were actually measured."
          },
          "estimates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttenuationEstimate"
            },
            "description": "Present when `material` and `frequency` were requested: calculated estimates, best confidence first. Each says how it was calculated, from which measurements, and how far they are."
          },
          "model_estimates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttenuationModelEstimate"
            },
            "description": "Present when `material` and `frequency` were requested: standard-formula and physics-model estimates, apart from measurements."
          }
        }
      },
      "AttenuationEstimate": {
        "type": "object",
        "description": "A calculated stand-in for a frequency nothing was measured at. Never a measurement; always with its confidence and distance from the evidence it came from.",
        "required": [
          "id",
          "method",
          "specimen",
          "kind",
          "frequency_ghz",
          "loss_db",
          "confidence",
          "confidence_reasons",
          "distance",
          "anchors",
          "model",
          "cross_check",
          "notes"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "method": {
            "type": "string",
            "enum": [
              "interpolated",
              "scaled"
            ],
            "description": "`interpolated`: between two measurements of the same specimen that bracket the frequency (linear in log-frequency). `scaled`: one measurement moved to the frequency by a physics model's change in loss at the specimen's thickness."
          },
          "specimen": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "panel",
              "assembly",
              "model"
            ]
          },
          "thickness_mm": {
            "type": [
              "number",
              "null"
            ]
          },
          "condition": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "dry",
              "wet",
              null
            ]
          },
          "polarization": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "co",
              "cross",
              null
            ]
          },
          "frequency_ghz": {
            "type": "number"
          },
          "loss_db": {
            "type": "number"
          },
          "confidence": {
            "type": "string",
            "enum": [
              "high",
              "moderate",
              "low",
              "very_low"
            ],
            "description": "Deterministic from distance and method: interpolation within 35% of an anchor is `high`, within 2x `moderate`; scaling is one tier lower than interpolation at the same distance; built assemblies, standard-slope scaling, and bands that disagree beyond the physics each lower it one more tier."
          },
          "confidence_reasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Why this tier, in words."
          },
          "distance": {
            "type": "object",
            "required": [
              "nearest_anchor_ghz",
              "ratio",
              "percent",
              "bracketed"
            ],
            "properties": {
              "nearest_anchor_ghz": {
                "type": "number"
              },
              "ratio": {
                "type": "number",
                "description": "The larger of (anchor / requested) and (requested / anchor); 1 means measured at the frequency."
              },
              "percent": {
                "type": "number",
                "description": "(ratio - 1) * 100; compare with `max_distance_pct`."
              },
              "bracketed": {
                "type": "boolean",
                "description": "True when measurements lie on both sides of the frequency."
              }
            }
          },
          "anchors": {
            "type": "array",
            "description": "The measurements the value was calculated from, with their sources.",
            "items": {
              "type": "object",
              "required": [
                "record",
                "source",
                "locator",
                "ghz",
                "loss_db"
              ],
              "properties": {
                "record": {
                  "type": "string"
                },
                "source": {
                  "type": "string"
                },
                "locator": {
                  "type": "string"
                },
                "ghz": {
                  "type": "number"
                },
                "loss_db": {
                  "type": "number"
                }
              }
            }
          },
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "The model used to scale a measurement across frequency; null for plain interpolation."
          },
          "cross_check": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "method",
                  "loss_db",
                  "difference_db"
                ],
                "properties": {
                  "method": {
                    "type": "string",
                    "const": "scaled"
                  },
                  "loss_db": {
                    "type": "number"
                  },
                  "difference_db": {
                    "type": "number",
                    "description": "This estimate minus the model-scaled one. A large value means the methods disagree; trust it less."
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AttenuationEstimateSummary": {
        "type": "object",
        "required": [
          "max_distance_pct",
          "record_count",
          "best_confidence",
          "loss_db",
          "by_confidence"
        ],
        "properties": {
          "max_distance_pct": {
            "type": "number"
          },
          "record_count": {
            "type": "integer"
          },
          "best_confidence": {
            "type": "string",
            "enum": [
              "high",
              "moderate",
              "low",
              "very_low"
            ]
          },
          "loss_db": {
            "$ref": "#/components/schemas/AttenuationLossRange"
          },
          "by_confidence": {
            "type": "object",
            "description": "The same spread tier by tier; filter here to apply your own tolerance.",
            "properties": {
              "high": {
                "type": "object",
                "required": [
                  "record_count",
                  "loss_db"
                ],
                "properties": {
                  "record_count": {
                    "type": "integer"
                  },
                  "loss_db": {
                    "$ref": "#/components/schemas/AttenuationLossRange"
                  }
                }
              },
              "moderate": {
                "type": "object",
                "required": [
                  "record_count",
                  "loss_db"
                ],
                "properties": {
                  "record_count": {
                    "type": "integer"
                  },
                  "loss_db": {
                    "$ref": "#/components/schemas/AttenuationLossRange"
                  }
                }
              },
              "low": {
                "type": "object",
                "required": [
                  "record_count",
                  "loss_db"
                ],
                "properties": {
                  "record_count": {
                    "type": "integer"
                  },
                  "loss_db": {
                    "$ref": "#/components/schemas/AttenuationLossRange"
                  }
                }
              },
              "very_low": {
                "type": "object",
                "required": [
                  "record_count",
                  "loss_db"
                ],
                "properties": {
                  "record_count": {
                    "type": "integer"
                  },
                  "loss_db": {
                    "$ref": "#/components/schemas/AttenuationLossRange"
                  }
                }
              }
            }
          }
        }
      },
      "AttenuationKindSummary": {
        "type": "object",
        "required": [
          "record_count",
          "loss_db",
          "thickness_mm"
        ],
        "properties": {
          "record_count": {
            "type": "integer"
          },
          "loss_db": {
            "$ref": "#/components/schemas/AttenuationLossRange"
          },
          "thickness_mm": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AttenuationLossRange"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "AttenuationSource": {
        "type": "object",
        "required": [
          "id",
          "citation",
          "url",
          "evidence",
          "frequency_range_ghz",
          "method",
          "license",
          "known_gaps",
          "sha256"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "citation": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "doi": {
            "type": [
              "string",
              "null"
            ]
          },
          "evidence": {
            "type": "string",
            "enum": [
              "measurement",
              "measured_fit",
              "standard_model",
              "physics_model"
            ]
          },
          "frequency_range_ghz": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2
          },
          "method": {
            "type": "string",
            "description": "How the numbers were obtained."
          },
          "license": {
            "type": "string"
          },
          "known_gaps": {
            "type": "string",
            "description": "What this source does not cover, or where it is weakest."
          },
          "sha256": {
            "type": "string",
            "description": "Hash of the exact document the values were read from."
          }
        }
      },
      "BuildingMaterialAttenuationInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [],
        "properties": {
          "material": {
            "type": "string",
            "enum": [
              "drywall",
              "ceiling-tile",
              "engineered-board",
              "plywood",
              "wood",
              "concrete",
              "reinforced-concrete",
              "concrete-block",
              "lightweight-concrete",
              "brick",
              "masonry-composite",
              "glass",
              "low-e-glass",
              "whiteboard",
              "foam",
              "cloth-partition",
              "metal",
              "rebar-mesh"
            ],
            "description": "Material class id. Omit to get every material's coverage and, with `frequency`, its measured range. Aliases such as plasterboard or cinder block are listed under `materials[].aliases`; use the id.",
            "examples": [
              "drywall"
            ]
          },
          "frequency": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Frequency to look up, in `frequency_unit`. Between 100 MHz and 500 GHz. Omit to see what each record spans.",
            "examples": [
              6.5
            ]
          },
          "frequency_unit": {
            "type": "string",
            "enum": [
              "MHz",
              "GHz"
            ],
            "default": "GHz",
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "examples": [
              "GHz"
            ]
          },
          "estimates": {
            "type": "string",
            "enum": [
              "auto",
              "all",
              "none"
            ],
            "default": "all",
            "description": "Calculated estimates for specimens with no measurement at the frequency, kept apart from measurements. `all` (default): beside any measurements, so a thick wall is not hidden by a thin panel's measurement. `auto`: only when no measurement at all covers the frequency. `none`: measurements only. Needs `frequency`; detail is returned when `material` is given. Defaults to all.",
            "examples": [
              "all"
            ]
          },
          "max_distance_pct": {
            "type": "number",
            "minimum": 0,
            "maximum": 400,
            "default": 50,
            "description": "How far from a measurement an estimate may be, as a percentage of the frequency: 50 accepts anchors between 0.67x and 1.5x of it. Raise it for more (lower-confidence) estimates, lower it to be strict; 0 returns none. Every estimate reports its own distance and confidence. Unit: %. Defaults to 50.",
            "examples": [
              50
            ]
          },
          "thickness_mm": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 2000,
            "description": "Your wall's thickness, used only for the ITU-R P.2040 slab model estimate. Measured records keep the thickness they were measured at. Requires `frequency`. Unit: mm.",
            "examples": [
              13
            ]
          }
        }
      },
      "BuildingMaterialAttenuationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [],
        "properties": {
          "material": {
            "type": "string",
            "enum": [
              "drywall",
              "ceiling-tile",
              "engineered-board",
              "plywood",
              "wood",
              "concrete",
              "reinforced-concrete",
              "concrete-block",
              "lightweight-concrete",
              "brick",
              "masonry-composite",
              "glass",
              "low-e-glass",
              "whiteboard",
              "foam",
              "cloth-partition",
              "metal",
              "rebar-mesh"
            ],
            "description": "Material class id. Omit to get every material's coverage and, with `frequency`, its measured range. Aliases such as plasterboard or cinder block are listed under `materials[].aliases`; use the id.",
            "examples": [
              "drywall"
            ]
          },
          "frequency": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Frequency to look up, in `frequency_unit`. Between 100 MHz and 500 GHz. Omit to see what each record spans.",
            "examples": [
              6.5
            ]
          },
          "frequency_unit": {
            "type": "string",
            "enum": [
              "MHz",
              "GHz"
            ],
            "default": "GHz",
            "description": "Unit of `frequency`: MHz or GHz. Defaults to GHz.",
            "examples": [
              "GHz"
            ]
          },
          "estimates": {
            "type": "string",
            "enum": [
              "auto",
              "all",
              "none"
            ],
            "default": "all",
            "description": "Calculated estimates for specimens with no measurement at the frequency, kept apart from measurements. `all` (default): beside any measurements, so a thick wall is not hidden by a thin panel's measurement. `auto`: only when no measurement at all covers the frequency. `none`: measurements only. Needs `frequency`; detail is returned when `material` is given. Defaults to all.",
            "examples": [
              "all"
            ]
          },
          "max_distance_pct": {
            "type": "number",
            "minimum": 0,
            "maximum": 400,
            "default": 50,
            "description": "How far from a measurement an estimate may be, as a percentage of the frequency: 50 accepts anchors between 0.67x and 1.5x of it. Raise it for more (lower-confidence) estimates, lower it to be strict; 0 returns none. Every estimate reports its own distance and confidence. Unit: %. Defaults to 50.",
            "examples": [
              50
            ]
          },
          "thickness_mm": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 2000,
            "description": "Your wall's thickness, used only for the ITU-R P.2040 slab model estimate. Measured records keep the thickness they were measured at. Requires `frequency`. Unit: mm.",
            "examples": [
              13
            ]
          },
          "hints": {
            "type": "boolean",
            "default": true,
            "description": "Set to `false` to leave out `next_steps`, the single pointer that is included when a result has a real gap, such as a free-space link budget that excludes walls. Defaults to `true`."
          }
        },
        "description": "Inputs for BuildingMaterialAttenuation. Same fields as the query string."
      },
      "BuildingMaterialAttenuationResponse": {
        "type": "object",
        "required": [
          "tool",
          "inputs",
          "result",
          "assumptions",
          "source"
        ],
        "properties": {
          "tool": {
            "type": "string",
            "const": "building-material-attenuation"
          },
          "inputs": {
            "$ref": "#/components/schemas/BuildingMaterialAttenuationInput",
            "description": "The inputs as used, with defaults applied."
          },
          "result": {
            "type": "object",
            "required": [
              "query",
              "dataset",
              "how_to_read",
              "materials",
              "sources"
            ],
            "properties": {
              "query": {
                "type": "object",
                "required": [
                  "material",
                  "frequency_ghz",
                  "thickness_mm",
                  "estimates",
                  "max_distance_pct"
                ],
                "properties": {
                  "material": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "frequency_ghz": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "thickness_mm": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "estimates": {
                    "type": "string",
                    "enum": [
                      "auto",
                      "all",
                      "none"
                    ]
                  },
                  "max_distance_pct": {
                    "type": "number"
                  }
                }
              },
              "dataset": {
                "type": "object",
                "required": [
                  "version",
                  "record_count",
                  "source_count",
                  "material_count"
                ],
                "properties": {
                  "version": {
                    "type": "string"
                  },
                  "record_count": {
                    "type": "integer"
                  },
                  "source_count": {
                    "type": "integer"
                  },
                  "material_count": {
                    "type": "integer"
                  }
                }
              },
              "how_to_read": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "How to interpret ranges, panels versus assemblies, nearby records and models. Read before quoting a number."
              },
              "materials": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AttenuationMaterial"
                },
                "description": "The requested material, or every material when none was requested (then without per-record detail)."
              },
              "sources": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AttenuationSource"
                },
                "description": "Full citations for exactly the sources the response draws on. Cite these when you use a number."
              }
            }
          },
          "assumptions": {
            "type": "string",
            "description": "The limitations of the data. Read before relying on a number."
          },
          "reference": {
            "type": "string",
            "description": "The dataset version and where its method and sources are documented."
          },
          "next_steps": {
            "type": "array",
            "maxItems": 1,
            "items": {
              "$ref": "#/components/schemas/NextStep"
            },
            "description": "Present only when the result has a real gap and `hints` is not `false`."
          },
          "source": {
            "$ref": "#/components/schemas/Source"
          }
        }
      }
    }
  }
}
