Hazmat and temperature-controlled storage¶
Hazmat regulation (UN/DOT classes, packing groups, marine-pollutant flags) and temperature-controlled storage (cold chain, frozen, ambient, room-temperature) are common operational requirements across pharma, food + beverage, chemicals, cosmetics, and any electronics shipment that contains a lithium battery. Per WMS ADR-0034, the v1 ingest API accepts the schema for both classes; the putaway + segregation Rules that consume the schema activate in v1.4 with flow-service.
This page documents the v1 schema. Operational behavior beyond schema acceptance is described in Future roadmap.
Per-SKU hazmat profile¶
The SKU master accepts an optional hazmat_profile JSONB attribute. Nullable — most SKUs are not hazmat — so an empty profile is the common case.
POST /wms-ingest/v1/master/skus?mode=upsert
{
"sku": {
"source_id": "ACME-LIB-PACK-001",
"consignor_id": "acme-corp",
"hazmat_profile": {
"un_number": "UN3480",
"dot_class": "9",
"packing_group": "II",
"proper_shipping_name": "Lithium ion batteries",
"subsidiary_classes": [],
"limited_quantity_eligible": true,
"excepted_quantity_eligible": false,
"passenger_aircraft_forbidden": true,
"cargo_aircraft_only": true,
"iata_class": "9",
"marine_pollutant": false,
"emergency_contact_required": true,
"msds_url": "https://acme.example/msds/lib-pack-001.pdf",
"regulatory_notes": "Lithium-ion 18650 cells, UN tested per 38.3."
},
...
}
}
Common fields are documented; industry-specific subfields can extend without schema migration because the body is JSONB. The schema validates the shape of common fields (UN number format, packing-group enum) but does not validate that, say, the dot_class matches the un_number — that’s regulatory data the upstream owns.
Per-SKU temperature profile¶
Same shape — optional, nullable, JSONB:
{
"sku": {
"source_id": "PHARMA-VACCINE-001",
"consignor_id": "pharma-co",
"temperature_profile": {
"storage_min_celsius": 2,
"storage_max_celsius": 8,
"transport_min_celsius": 2,
"transport_max_celsius": 8,
"frozen_acceptable": false,
"excursion_max_minutes_at_25c": 60,
"stability_class": "refrigerated-2-8",
"monitoring_required": true,
"monitoring_frequency_minutes": 15,
"label_requirements": "Keep refrigerated. Do not freeze."
},
...
}
}
A SKU may carry both hazmat_profile and temperature_profile (cold-chain pharma, certain reagents). The two profiles are independent attribute groups.
Per-Zone allowed classes¶
The Location master accepts hazmat + temperature attributes on Zones (not on Bins). Bins inherit from their parent Zone. Warehouses do not carry the attributes directly.
POST /wms-ingest/v1/master/locations?mode=upsert
{
"location": {
"source_id": "ZONE-COLD-01",
"kind": "ZONE",
"parent_ref": "wh-tokyo-01",
"hazmat_classes_allowed": ["3", "8", "9"], ◄── DOT classes; empty array means "no hazmat allowed"
"temperature_range_celsius": {
"min": 2,
"max": 8
},
"consignor_assignment": null,
...
}
}
Several semantics:
hazmat_classes_allowed: []— no hazmat (default for a normal zone)hazmat_classes_allowed: ["*"]— any hazmat class (a general hazmat zone; rare; subject to segregation matrix at the Rule layer in v1.4)hazmat_classes_allowed: ["3", "9"]— exactly those DOT classestemperature_range_celsius: null— ambient (whatever the warehouse provides)temperature_range_celsius: { "min": -25, "max": -15 }— frozen storage zone
A Bin under a Zone inherits both. You cannot override hazmat allowance or temperature range on a Bin in v1 — keep the hierarchy clean.
v1 schema acceptance vs. v1.4 Rule enforcement¶
In v1 (Phases 1–7), the ingest API accepts the schema and stores it. Putaway and pick Rules do not yet evaluate hazmat or temperature compatibility — a v1 tenant manually places hazmat SKUs in the right zones using zone naming conventions and operator discipline. The schema is the substrate for v1.4 behavior.
In v1.4 (flow-service per WMS ADR-0029), Rule conditions activate:
Putaway: a SKU with
hazmat_profile.dot_class = "9"is rejected from a zone where"9" ∉ hazmat_classes_allowed. Suggest the closest qualifying zone.Putaway: a SKU with
temperature_profile.storage_max_celsius < 8is rejected from a zone whosetemperature_range_celsius.maxexceeds 8.Pick: picking from a temperature-non-compliant overflow location is gated and surfaces in the supervisor queue.
Segregation matrix: certain DOT class pairs (oxidizers + flammables, for instance) cannot share a zone even if both classes are individually allowed; the v1.4 Rule layer ships a default matrix and lets tenants override.
The contract surface stays identical; only the runtime evaluation changes. v1 callers should populate hazmat_profile and temperature_profile from launch so the v1.4 cutover does not require a master-data re-ingest.
Cold-chain monitoring via IoT¶
Active monitoring of cold-chain assets in transit and storage flows through WES as an extended SubsystemArtifact (per WMS ADR-0035), not through this ingest API. The ingest API records the monitoring_required and monitoring_frequency_minutes flags as part of the SKU’s temperature_profile, but the actual sensor stream lives in WES.
When a temperature excursion is detected by WES, it publishes a cold-chain.excursion event. The platform consumes this and emits inventory.adjusted webhooks with an excursion reason code — that’s the seam visible to ingest API consumers.
Anti-patterns¶
Putting hazmat or temperature attributes on a Bin. Use the parent Zone. Bins inherit; the v1.4 Rule engine reads from the Zone.
Adding a custom hazmat_class outside DOT. The schema validates the well-known DOT/UN classification. If you have a non-DOT classification (regional, internal), put it in regulatory_notes or subsidiary_classes.
Sending a SKU with conflicting profiles (e.g., frozen_acceptable: false but storage_min_celsius: -20). The schema does not validate the cross-field consistency in v1; the v1.4 Rule engine will catch it. Better to fix at the source.
Expecting v1 segregation enforcement. v1 only stores; v1.4 enforces. Plan operational workarounds for the v1 window if your industry requires strict segregation.