This recipe walks through a monthly ZDHC compliance audit against the chemicals you track in Conforma.
1. Register a chemical
curl -X POST {{BASE_URL}}/api/v1/chemicals \-H "Content-Type: application/json" \-d '{"name": "Reactive Dye Red B","casNumber": "12236-82-7","description": "Azo reactive dye for cellulosic fibers","zdhcLevel": "LEVEL_3","active": true}'
2. List all active chemicals in your catalog
curl {{BASE_URL}}/api/v1/chemicals
3. Group by ZDHC level
Call the list endpoint once per level:
curl "{{BASE_URL}}/api/v1/chemicals?zdhcLevel=NOT_REGISTERED"curl "{{BASE_URL}}/api/v1/chemicals?zdhcLevel=LEVEL_1"curl "{{BASE_URL}}/api/v1/chemicals?zdhcLevel=LEVEL_2"curl "{{BASE_URL}}/api/v1/chemicals?zdhcLevel=LEVEL_3"
Or do it client-side in a single request:
const all = await fetch(`${BASE_URL}/api/v1/chemicals`).then(r => r.json());const byLevel = all.reduce((acc, c) => {(acc[c.zdhcLevel] ??= []).push(c);return acc;}, {});console.table({NOT_REGISTERED: byLevel.NOT_REGISTERED?.length ?? 0,LEVEL_1: byLevel.LEVEL_1?.length ?? 0,LEVEL_2: byLevel.LEVEL_2?.length ?? 0,LEVEL_3: byLevel.LEVEL_3?.length ?? 0,});
4. Build a phase-out priority list
Any chemical still active = true and classified NOT_REGISTERED is a candidate for substitution:
const phaseOut = all.filter(c => c.active && c.zdhcLevel === 'NOT_REGISTERED');
For each candidate, schedule an internal review and, once replaced, flag it as inactive:
curl -X PATCH "{{BASE_URL}}/api/v1/chemicals/$CHEM_ID" \-H "Content-Type: application/json" \-d '{ "active": false, "description": "Replaced by ReactiveDyeRedC — see ticket CHEM-123" }'
5. Tie chemicals to suppliers
Chemicals are catalog entries only — the purchase history of a chemical batch is captured via the general Purchases API. Create a purchase order as usual, with the supplier that provided the chemical.
Compliance scorecard
A minimal dashboard can be built from two numbers:
- % of active chemicals with
zdhcLevel = LEVEL_3→ green - % of active chemicals with
zdhcLevel = NOT_REGISTERED→ red, phase-out queue
const active = all.filter(c => c.active);const score = active.filter(c => c.zdhcLevel === 'LEVEL_3').length / active.length;
Match chemicals across systems using casNumber — it's the unambiguous identifier. Commercial name alone is unreliable.