---
title: "ADR 1.1.3.6 Exemption Calculator (multiple items)"
method: POST
path: "/api/adr-calculator"
tags: ["Reference Data", "ADR"]
---

# ADR 1.1.3.6 Exemption Calculator (multiple items)

`POST /api/adr-calculator`

## Request

- Base URL: `https://www.freightutils.com`
- URL: `https://www.freightutils.com/api/adr-calculator`
- Auth: none declared

Check a load carried in packages against the ADR 1.1.3.6 small-load exemption: the 1.1.3.6.4 points sum within 1,000, each 1.1.3.6.3 category maximum (a total when every line is one category) and no category 0. items[].state names each line's route.

## Request body

- AdrCalculatorRequest: 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.
  - `items` object[], required: Array of dangerous goods items to calculate.
    - `un_number` string, required: UN number of the substance — or an ID-prefixed air-only number (e.g. ID8000), routed AIR_ONLY_ID with nothing counted — the ADR answer for limited-quantity packages to the ICAO TI (3.4.9, 3.4.10, 1.1.3.6.5), as the line's warning states.
    - `quantity` number, required: Quantity of the dangerous goods in kg or litres, as a JSON number > 0 and ≤ 1e9. Text ("300 kg"), null, 0 or a negative value is refused with a 400 — never scored.
    - `packing_group` 'I' | 'II' | 'III': 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` integer: 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` 'L' | 'kg': 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` 'net' | 'gross': OPTIONAL. A figure declared gross returns no points in any unit — send the net quantity. Any other value is a 400.

## Response `200`

ADR 1.1.3.6 result; items[].state names each line's route (COUNTED, AIR_ONLY_ID, NOT_SUBJECT_TO_ADR, BLOCKED). All lines ambiguous: human_review_required + blocking_errors[AMBIGUOUS_UN_VARIANT] + candidates[], no verdict; a mixed load withholds that line only. Agreeing rows resolve.

- AdrCalculatorResponse
  - `blocking_errors` object[]: Present only on the all-ambiguous 200 (no verdict): [{ code: AMBIGUOUS_UN_VARIANT, message, recovery }], in the plain body too. recovery.params never pick a row (sent unchanged: asked again); pick from candidates[] by packing group, concentration or description; send packing_group or variant_index.
    - `code` string
    - `message` string
    - `recovery` object
  - `items` AdrCalculatorItem[], required
    - `un_number` string, required: UN number of the substance — or, on an air-only line, the canonical ID number (e.g. ID8000).
    - `state` 'COUNTED' | 'AIR_ONLY_ID' | 'NOT_SUBJECT_TO_ADR' | 'BLOCKED', required: The line's route, in the document check's words: COUNTED (evaluated on one Table A row), AIR_ONLY_ID (ID-prefixed, no Table A entry, nothing counted — its warning states the limited-quantity condition), NOT_SUBJECT_TO_ADR, BLOCKED (no points from the line as sent).
    - `proper_shipping_name` string, nullable, required: Official ADR proper shipping name. Null on a withheld line (no row was pinned — never the first row's name).
    - `class` string, nullable, required: ADR hazard class. Null on a withheld line whose candidate rows differ in class.
    - `transport_category` string, nullable, required: Transport category (0-4). Null on a withheld line.
    - `packing_group` string, nullable: The resolved ADR Table A packing group for this row (echoed so a multi-variant resolution is traceable). On a withheld line: the packing group the caller supplied, or null.
    - `variant_index` integer, nullable: The resolved ADR Table A variant index for this row. Null on a withheld line.
    - `quantity` number, required: Quantity being transported.
    - `multiplier` integer, nullable, required: Points multiplier for this transport category. Null for out-of-scope rows (not_subject_to_adr / carriage_prohibited) and withheld lines — they never enter the points math.
    - `points` number, nullable, required: Calculated points (quantity x multiplier). Null for out-of-scope rows, basis mismatches and withheld lines.
    - `quantity_unit` 'L' | 'kg' | 'null', nullable: Echo of the unit the caller declared (null when none was declared).
    - `quantity_basis` 'net' | 'gross' | 'null', nullable: Echo of the basis the caller declared (null when none was declared).
    - `expected_unit` 'L' | 'kg' | 'null', nullable: The dimension ADR 1.1.3.6.3 counts this row in — L for liquids and compressed gases, kg for solids, liquefied/refrigerated/dissolved gases and articles; null when the row does not pin one.
    - `basis_mismatch` boolean: Present and true when the declared unit or basis cannot be a 1.1.3.6.3 quantity for this row. The line never enters the points math; total_points and exempt are null (unless the load is already settled by other lines).
    - `category_unresolved` boolean: Present and true when the row carries no transport category in Table A column (15) — a dash (e.g. UN 3166) or 'See SP 671' (UN 3316 kits): no 1.1.3.6 multiplier, so points, total_points and exempt are null and a warning names the column value.
    - `withheld` boolean: Present and true when this line's UN resolves to more than one ADR Table A row and nothing pinned one. Row fields are null; the candidate rows sit under the response's candidates[] tagged with this line's item_index. total_points and exempt are null while any line is withheld.
    - `ambiguity_reason` 'multiple_variants' | 'packing_group_collision' | 'no_matching_variant': Why the withheld line could not be pinned. Present only with withheld.
    - `equivalent_variants` object[]: Present when the supplied packing_group left several Table A rows that agree on class, transport category, scope and the 1.1.3.6.3 counted dimension: the rows this verdict holds for. The row fields above are the first one's.
      - `variant_index` integer
      - `proper_shipping_name` string
    - `variant_note` string: Plain-language statement of the equivalence: which rows, which fields they share, whose fields are shown. Present only with equivalent_variants.
    - `not_subject_to_adr` boolean: Present and true when this row is NOT SUBJECT TO ADR (Table A) — excluded from the 1.1.3.6 calculation.
    - `carriage_prohibited` boolean: Present and true when this row is CARRIAGE PROHIBITED (Table A) — the load cannot be carried by road under ADR.
    - `conditions_ref` string: ADR section whose carriage conditions still apply to a not-subject row (e.g. 5.5.3).
    - `air_only_id` boolean: Present and true on an ID-prefixed air-only line: no ADR Table A entry, row fields null, never in the points — the ADR answer for limited-quantity packages to the ICAO TI (3.4.9, 3.4.10, 1.1.3.6.5), stated in the warning. Never not_subject_to_adr.
  - `total_points` number, nullable, required: Sum of all item points. Null when no load verdict was reached — a line is withheld (items[].withheld), declared on a basis 1.1.3.6.3 does not count (items[].basis_mismatch), or carries no transport category in Table A column (15) (items[].category_unresolved).
  - `threshold` integer, required: 1.1.3.6 threshold (always 1000).
  - `exempt` boolean, nullable, required: Whether the shipment qualifies for 1.1.3.6 exemption. Null when no load verdict was reached, never false as a stand-in. false with total_points null = already NOT exempt on the resolved lines (carriage prohibited, category 0, a maximum exceeded, or a partial sum over 1,000) while a line is withheld.
  - `has_category_zero` boolean, required: Whether any item is transport category 0 (never exempt).
  - `has_quantity_exceedance` boolean, required: Whether any single item exceeds its maximum quantity per transport unit.
  - `warnings` string[], required: Any warnings about the calculation.
  - `message` string, required: Human-readable verdict. For loads whose entries are outside ADR scope this is a dedicated scope verdict (e.g. 'Not subject to ADR (road). Section 5.5.3 applies: ...' or 'Carriage prohibited by ADR (road). ...'), never a 1.1.3.6 claim.
  - `not_subject_to_adr` boolean: Present and true when EVERY item in the load is NOT SUBJECT TO ADR (Table A) — the 1.1.3.6 calculation was not applied.
  - `conditions_ref` string: ADR section whose carriage conditions still apply to a not-subject load (e.g. 5.5.3 for dry ice).
  - `conditions` object[]: Verbatim ADR 2025 carriage conditions for conditions_ref (e.g. section 5.5.3: ventilation, package marking, vehicle warning mark, documentation, unpackaged dry ice, training).
    - `ref` string, required: ADR 2025 subsection.
    - `heading` string, required: Short practitioner-facing heading.
    - `requirement` string, required: Verbatim requirement text from ADR 2025.
    - `note` string: Verbatim accompanying note/example from ADR 2025, where one exists.
  - `carriage_prohibited` boolean: Present and true when the load contains a CARRIAGE PROHIBITED Table A entry — exempt is false and the load may not be carried by road under ADR, whether or not another line is withheld.
  - `human_review_required` boolean: Present and true when at least one line is withheld for an unpinned multi-variant UN. candidates[] then lists that line's rows.
  - `candidates` object[]: The withheld lines' candidate Table A rows, each tagged with the item_index and un_number of the line it belongs to. Present only with human_review_required.
    - `item_index` integer
    - `un_number` string
    - `variant_index` integer
    - `packing_group` string, nullable
    - `proper_shipping_name` string
    - `transport_category` string, nullable
    - `multiplier` integer, nullable
    - `expected_unit` 'L' | 'kg' | 'null', nullable: The 1.1.3.6.3 counted dimension of the candidate row — the only difference between two candidates when a packing group collides on state alone (UN 3375 PG II: liquid in L, solid in kg).
  - `_source` SourceEnvelope: Additive provenance citation for the dataset or method behind this response, from FreightUtils' single provenance source of truth. Shapes: reference, methodology (calculators), or reference with provenance_status:'pending-verification'. Fields appear only when a real value exists.
    - `type` 'reference' | 'methodology', required
    - `authority` string
    - `dataset` string
    - `source_url` string
    - `data_vintage` string
    - `licence` string
    - `retrieved_via` string
    - `standard` string
    - `reference` string
    - `computed_by` string
    - `provenance_status` 'pending-verification'
    - `dataset_edition` string: Edition this response is served from (editioned reference datasets only), e.g. 'UN/LOCODE 2025-1'.
    - `authority_current_edition` string: The authority's current in-force edition at the last check. Differs from dataset_edition only when the served data lags the authority.
    - `checked_at` string: YYYY-MM-DD of the last served-vs-authority edition comparison (on-demand checker, staleness-guarded).

## Other responses

- `400`: Invalid input — e.g. a quantity that is not a number > 0, an unknown unit or basis, an unknown UN number, a single-substance field sent beside items[], or any field the route does not read — refused by name, never dropped. The error names what arrived.

---

[API](https://skmtc.dev/freightutils/apis/freightutils-api.md) · [All operations](https://skmtc.dev/freightutils/apis/freightutils-api/llms.txt) · [OpenAPI document](https://skmtc.dev/freightutils/apis/freightutils-api/revisions/6607bb3cea90?raw)
