{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/freightutils-com/json-schema/freightutils-com-adr-calculator-request-schema.json",
  "title": "AdrCalculatorRequest",
  "description": "items[] carries every line. Any single-substance field — un_number, quantity, packing_group, variant_index, unit, basis (or un / qty) — sent at the top level beside items[] is refused with HTTP 400 naming it: never dropped, never applied to a line by guess.",
  "x-generated": "2026-11-09",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/freightutils-com-openapi.json#/components/schemas/AdrCalculatorRequest",
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "description": "Array of dangerous goods items to calculate.",
      "items": {
        "type": "object",
        "properties": {
          "un_number": {
            "type": "string",
            "description": "UN number of the substance — or an ID-prefixed air-only number (e.g. ID8000), no Table A entry: counted out of 1.1.3.6 (AIR_ONLY_ID) only with icao_limited_quantity: true (ICAO TI limited quantities, 3.4.9/3.4.10); otherwise BLOCKED."
          },
          "quantity": {
            "type": "number",
            "description": "Quantity in kg or litres, a JSON number ≥ 0 and ≤ 1e9. 0 means not carried: the line is left out (EXCLUDED_NIL) and named in a warning; all zeros is a 400. Text (\"300 kg\"), null or a negative value is a 400 — never scored.",
            "maximum": 1000000000,
            "minimum": 0
          },
          "packing_group": {
            "type": "string",
            "enum": [
              "I",
              "II",
              "III"
            ],
            "description": "Packing group — only needed for a UN number with more than one ADR Table A row (e.g. UN 1789); ignored for single-row UNs. Without it that line is withheld while its siblings are answered. Enough alone when the rows it leaves agree on the verdict fields (UN 1790 PG I)."
          },
          "variant_index": {
            "type": "integer",
            "minimum": 0,
            "description": "ADR Table A variant index (as returned by /api/adr) — pins one row when a UN number has several variants sharing a packing group (concentration bands). Ignored for single-row UN numbers. A JSON integer; anything else is a 400."
          },
          "unit": {
            "type": "string",
            "enum": [
              "L",
              "kg"
            ],
            "description": "OPTIONAL dimension of quantity, checked against the 1.1.3.6.3 basis for the row. Case-insensitive; any other value is a 400."
          },
          "basis": {
            "type": "string",
            "enum": [
              "net",
              "gross"
            ],
            "description": "OPTIONAL. A gross mass on an entry counted by mass is used only as an upper bound of the net mass, and the load is exempt on it only if it clears even so (figure_basis upper_bound_gross); otherwise, and for litres entries, no points. Any other value is a 400."
          },
          "icao_limited_quantity": {
            "type": "boolean",
            "description": "OPTIONAL, on an ID-prefixed line only (on a UN-numbered line it is a 400): true states that the packages are limited quantities conforming to the ICAO Technical Instructions (the 3.4.8 mark, or 3.4.10). Only then is the line counted out of 1.1.3.6 (1.1.3.4.2, 1.1.3.6.5)."
          }
        },
        "required": [
          "un_number",
          "quantity"
        ],
        "additionalProperties": false
      }
    },
    "edition": {
      "type": "string",
      "enum": [
        "2025",
        "2027"
      ],
      "description": "ADR edition \"2025\" or \"2027\"; wins over as_of. Omitted, as_of picks it: before 2027-01-01 ADR 2025; to 2027-06-30 ADR 2027 with an EDITION_TRANSITIONAL notice (ADR 2025 still allowed, 1.6.1.1); from 2027-07-01 ADR 2027 only (2025 refused, EDITION_UNSUPPORTED, 422). /api-docs#adr-editions"
    },
    "as_of": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "YYYY-MM-DD the answer is for (default today, UTC). Picks the ADR edition when edition is omitted; echoed in adr_edition, the 1.2 envelope and the check receipt."
    }
  },
  "required": [
    "items"
  ],
  "additionalProperties": false
}