EU compliance checks
EU compliance checks
qr3.app ships with a built-in EU validator for Digital Product Passports. It checks your DPPs against the relevant EU frameworks (ESPR, Loi AGEC) and returns structured issues with severity, field path, hints, and an optional ESPR-ready badge.
There are three entry points:
- Server-side, for persisted DPPs —
GET /v1/dpp/:id/eu-compliance - Live preview in the create form —
POST /v1/dpp/validate - Live simulator on the detail page —
POST /v1/dpp/:id/validate-update
All three return the same EuComplianceResult shape — so you only need to implement the consumer once.
Result shape
{ "compliant": false, "espr_ready": true, "issues": [ { "code": "TEXTILE_AGEC_REQUIRED", "severity": "error", "field": "textile_data.country_weaving_knitting", "message": "AGEC chain country is required for products sold on the French market", "hints": { "market": "FR" } }, { "code": "TEXTILE_GREENWASHING", "severity": "warning", "field": "product_name", "message": "\"natural\" is a prohibited term under AGEC", "hints": { "term": "natural", "locale": "en", "category": "generic" } } ], "summary": { "errors": 1, "warnings": 1, "info": 0 }}compliant: true⇔summary.errors === 0espr_ready: true⇔ all ESPR opt-in fields set (see Textile DPP)issues[]is stably sorted by severity (error→warning→info), then by code
Severity levels
| Severity | Meaning | Example |
|---|---|---|
error | Blocks EU compliance; should be fixed before release | TEXTILE_AGEC_REQUIRED when FR is in market and origin chain is incomplete |
warning | Recommendation; customer may save, but should act | TEXTILE_GREENWASHING outside FR |
info | Status signal, no action required | TEXTILE_ESPR_READY — all ESPR opt-in fields complete |
Rule catalog (textile + battery)
Textile (Q3.3.4)
| Code | Severity | Trigger |
|---|---|---|
TEXTILE_AGEC_REQUIRED | error (FR) · warning (else) | AGEC chain (weave/dye/assembly) incomplete, FR in market |
TEXTILE_MICROPLASTICS_CONSISTENCY | error | Synthetics ≥ 50% but contains_microplastics !== true |
TEXTILE_SVHC_THRESHOLD | warning | SVHC entry without CAS number or concentration |
TEXTILE_GREENWASHING | error (FR) · warning (else) | Prohibited term in product_name/material |
TEXTILE_ESPR_READY | info | All ESPR fields B7–B15 complete |
Battery (Phase 4a)
The validator checks limits from the EU battery regulation 2023/1542 (recycled content thresholds, carbon footprint classes, minimum warranty, …). Details in the battery DPP docs.
Live preview in the create form
When you create a DPP in the dashboard (/dashboard/dpp/new), the same validator runs stateless on every validate roundtrip — no need to save the passport. The eu_compliance section appears directly under the existing validation panel and highlights errors and warnings before submit.
Direct API usage:
curl -X POST https://qr3.app/v1/dpp/validate \ -H "Authorization: Bearer $API_KEY" \ -H "X-Workspace-Id: $WS_ID" \ -H "Content-Type: application/json" \ -d '{ "category": "textile", "gtin": "04012345678901", "product_name": "Natural Cotton T-Shirt", "manufacturer": "EcoWear GmbH", "origin_country": "PT", "market_countries": ["FR"], "textile_data": { "fiber_composition": [{ "material": "cotton", "percentage": 100, "recycled_pct": 0 }] } }'The response contains:
data.valid— syntactic validity (Zod)data.errors[]— classic Zod errorsdata.jsonld_preview— JSON-LD previewdata.eu_compliance— EU validator result (nullwhen the Zod validation fails)
When eu_compliance.summary.errors > 0, the dashboard renders a save-guard banner right above the submit button — the save is not blocked, but you see the consequences before you click.
Live simulator on the detail page
On /dashboard/dpp/:dppId, below the compliance section, there is a simulator card. It lets you change the two most compliance-relevant fields (status and market_countries) and see the impact immediately — without touching persisted data. Typical flow:
- Preview: “What happens if I add FR to my markets?” → pick chip “FR” → click “Preview EU impact” →
TEXTILE_AGEC_REQUIREDbecomes visible - Fix: complete the origin chain (outside the simulator, via create or bulk flow)
- Save: “Save changes” persists status/market list via
PUT /v1/dpp/:id
Direct API usage:
curl -X POST https://qr3.app/v1/dpp/$DPP_ID/validate-update \ -H "Authorization: Bearer $API_KEY" \ -H "X-Workspace-Id: $WS_ID" \ -H "Content-Type: application/json" \ -d '{ "market_countries": ["DE", "FR"], "status": "live" }'The response extends the standard result with a preview object:
{ "data": { "compliant": false, "espr_ready": false, "issues": [/* … */], "summary": { "errors": 1, "warnings": 0, "info": 0 }, "preview": { "status": "live", "landing_locale": "en", "market_countries": ["DE", "FR"], "changed_fields": ["status", "market_countries"] } }}changed_fields[] lists only fields that would actually change — so your UI can highlight exactly those chips.
An empty body is allowed and returns the current compliance verdict (re-check the stored state).
Automation
CI pipeline
A typical CI check:
STATUS=$(curl -s -o /tmp/out.json -w "%{http_code}" \ https://qr3.app/v1/dpp/$DPP_ID/eu-compliance \ -H "Authorization: Bearer $API_KEY" \ -H "X-Workspace-Id: $WS_ID")
if [ "$STATUS" != "200" ]; then exit 1fi
jq -e '.data.summary.errors == 0' /tmp/out.json > /dev/nullMCP server
The bundled @qr3/mcp-server exposes a validate_dpp tool, among others — AI agents can use it to check, before any create call, whether the given data meets AGEC rules.
Common issues
| Issue | Cause | Fix |
|---|---|---|
TEXTILE_MICROPLASTICS_CONSISTENCY | Polyester share ≥ 50%, flag still false | Set contains_microplastics: true |
TEXTILE_AGEC_REQUIRED with hints.market: "FR" | FR in market, chain incomplete | Set country_weaving_knitting, country_dyeing_printing, country_assembly |
TEXTILE_GREENWASHING as error | Prohibited term + FR in market | Remove term from product_name or adjust market |
TEXTILE_SVHC_THRESHOLD | SVHC substance without CAS | Add the CAS number from the REACH list |
Next steps
- Textile DPP — schema, origin chain, microplastics warning
- Battery DPP — mandatory battery fields
- API reference — all EU compliance endpoints in detail