← veridyn.eu
HU EN DE

Entwickler-API v1

veridyn

Programmatischer Zugriff auf die Produktpässe — für Webshop-/ERP-Sync und Massenautomatisierung.

ab dem Pro-Tarif

Während der offenen Beta steht die API allen Beta-Konten zur Verfügung — die Pro-Voraussetzung gilt erst nach der Beta.

Authentifizierung

Jede Anfrage authentifiziert sich mit einem API-Schlüssel im Authorization-Header:

Authorization: Bearer vk_<Ihr Schlüssel>

Einen Schlüssel erstellen Sie unter Einstellungen → Entwickler-API. Den Klartext-Schlüssel zeigen wir nur einmal, bei der Erstellung — bewahren Sie ihn sicher auf. Der Schlüssel gewährt Lese- und Schreibzugriff auf alle Produkte Ihres Kontos, behandeln Sie ihn daher vertraulich.

Schnelltest im Browser

Der Schlüssel gehört ausschließlich in den Authorization-Header — aus Sicherheitsgründen nicht als URL-Parameter (er würde im Browserverlauf und in den Server-Logs landen). Probieren Sie es zum Beispiel mit curl:

curl -H "Authorization: Bearer vk_<Ihr Schlüssel>" https://veridyn.eu/api/v1/categories

Schlüssel-Geltungsbereich (Scope)

Beim Erstellen eines Schlüssels wählen Sie einen Geltungsbereich: Voll (Lesen + Schreiben) oder Nur Lesen (read-only). Ein read-only-Schlüssel darf ausschließlich GET aufrufen — bei POST/PATCH/DELETE lautet die Antwort 403 read_only. Für ERP- oder Anzeige-Integrationen verwenden Sie einen read-only-Schlüssel.

Anfragelimit (Rate Limit)

Pro Schlüssel je nach Tarif 120–600 Anfragen / Minute (Pro 120 · Business 300 · Enterprise 600; während der offenen Beta 300). Jede Antwort enthält die Header X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset (Unix-Zeit). Wird das Limit überschritten, lautet die Antwort 429 rate_limited mit dem Header Retry-After — warten Sie die angegebene Zeit ab und versuchen Sie es dann erneut.

Idempotenz

Sie können POST-Anfragen einen eindeutigen Idempotency-Key-Header mitgeben. Senden Sie die Anfrage mit demselben Schlüssel erneut (z. B. nach einem Netzwerkfehler), erhalten Sie die ursprüngliche Antwort zurück — es entsteht kein Duplikat. Eine wiederholte Antwort erkennen Sie am Header Idempotency-Replayed: true.

OpenAPI

Maschinenlesbare API-Beschreibung (OpenAPI 3.1) — für den Postman-Import und die SDK-/Codegenerierung:

https://veridyn.eu/api/v1/openapi.json

⚡ Interaktive API-Referenz

Durchsuchbare Endpunkt-Referenz zum Blättern — direkt aus der obigen Spezifikation gerendert.

DPP-Vokabular (JSON-LD-Namensraum)

Die maschinenlesbare Fassung des öffentlichen Passes ist JSON-LD: Die Standardfelder stammen von schema.org, die DPP-spezifischen Felder aus dem Namensraum dpp:. Dieser Namensraum ist auflösbar — öffnen Sie ihn, um die Bedeutung jedes Feldes zu sehen; mit dem Header Accept: application/ld+json liefert er das maschinenlesbare @context-Dokument.

https://veridyn.eu/ns/dpp/v1/

Ein einzelner Pass als Daten: die öffentliche URL mit ?format=jsonld (oder dem Header Accept: application/ld+json).

Postman-Collection

Fertige, importierbare Collection (mit voreingestellten Variablen und Beispielanfragen). Nach dem Import füllen Sie nur die Collection-Variablen base_url und api_key aus.

⬇ Postman-Collection herunterladen

Eigenes SDK generieren

Aus der OpenAPI-Spezifikation erzeugen Sie mit openapi-generator einen offiziellen Client für jede Sprache — kein handgepflegtes SDK, immer aktuell. Z. B. PHP:

npx @openapitools/openapi-generator-cli generate \
  -i https://veridyn.eu/api/v1/openapi.json \
  -g php -o ./veridyn-sdk

Der Wert von -g kann typescript-fetch, python, java und vieles mehr sein.

Schnellstart (Code)

PHP

$ch = curl_init('https://veridyn.eu/api/v1/products?limit=5');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => ['Authorization: Bearer vk_...'],
]);
$products = json_decode(curl_exec($ch), true)['data'];

JavaScript (fetch)

const res = await fetch('https://veridyn.eu/api/v1/products?limit=5', {
  headers: { 'Authorization': 'Bearer vk_...' }
});
const { data } = await res.json();

Basis-URL

https://veridyn.eu/api/v1

Die Antworten haben das Format application/json und sind UTF-8-kodiert.

Sandbox (Testkonto)

Integrationen entwickelt man nicht gegen das Echtkonto. Unter Einstellungen → Sandbox können Sie ein separates Testkonto mit eigenem API-Schlüssel anfordern: dieselbe API, dieselben Schemata, aber vollständig von den Echtdaten getrennt.

Endpunkte

MethodePfadBeschreibung
GET/categoriesVerfügbare Produktkategorien.
GET/schema/{category}Die vollständige Feldliste der Kategorie — Pflicht + optional, Typ, Enum, sprachabhängige Felder.
GET/productsListe der Produkte (Zusammenfassung). Parameter: q, status, page, limit — bzw. updated_since und cursor für die Nur-Änderungen-Synchronisation.
POST/productsNeuer Produktpass. Body: { category, data }.
POST/products:batchMassenanlage — bis zu 500 Produkte pro Anfrage. Body: { category, products: [ … ], on_duplicate? }. Liefert einen Bericht je Zeile.
GET/products/{id}Die vollständigen Daten eines Produkts + Pass-URL.
PATCH/products/{id}Aktualisierung (partiell) — erzeugt eine neue Version. Body: { data, change_type? }. change_type ist optional: correction (im Modell standen falsche Daten → bereits ausgegebene Chargen-/Stückpässe veralten und können aufgefrischt werden) oder change (das Produkt hat sich ab einem bestimmten Datum geändert → die Daten früherer Stücke bleiben korrekt und werden nicht überschrieben).
GET/products/{id}/qrQR-Code (SVG) für den öffentlichen Produktpass.
GET/eventsVerlauf der ausgehenden Webhook-Ereignisse (?after_id= & limit=) — für Duplikat-Filterung und das Nachholen verpasster Ereignisse.
GET/products/{id}/childrenListe der untergeordneten Chargen-/Stückpässe.
POST/products/{id}/childrenChargen-/Stückpass. Einzeln: { level, lot|serial, data } · als Masse: { level, items:[…] } (max. 500). Erbt die Daten des Elternpasses.
PATCH/products/{id}/childrenMassenaktualisierung von Kind-Pässen (max. 500): { items:[{ serial|lot|id, data }] }. Für Live-BMS-Daten — mit zeilenweisem Bericht.
POST/products/{id}/archive
/products/{id}/restore
Archivieren und Wiederherstellen. Bewusst kein PATCH: Eine Sichtbarkeitsänderung ist keine Datenänderung, daher entsteht keine leere Version im Änderungsverlauf. Kein Body nötig.
GET/products/{id}/versions
/products/{id}/versions/{no}
Der Änderungsverlauf: die Liste der Versionen bzw. der feldgenaue Unterschied einer bestimmten Version (was sich geändert hat, von welchem Wert zu welchem).
GET
POST
DELETE
/products/{id}/grants
/products/{id}/grants/{gid}
Ablaufende Zugriffslinks für eine Behörde / eine Partei mit berechtigtem Interesse — Zugang zu vertraulichen Feldern ohne Anmeldung. DELETE widerruft einen Link.
GET
POST
DELETE
/products/{id}/documents
/products/{id}/documents/{docId}
Zertifikate und Dokumente. Upload in base64 (PDF, PNG, JPEG, WebP), Download der Datei, Löschen. Das GET mit Dokument-Kennung liefert die Datei, nicht JSON.
GET
POST
/supplier-requestsLieferanten-Datenanfragen: Liste und Start. Die Antwort enthält den Lieferanten-Link.
GET
POST
DELETE
/supplier-requests/{rid}
/supplier-requests/{rid}/merge
Eine Datenanfrage mit den eingereichten Werten, das Einpflegen der Einreichung (neue Produktversion) bzw. der Widerruf.
POST/eo/tokenKurzlebiges JWT für die normkonforme Wirtschaftsakteur-Schnittstelle. Body: { ttl? }.
GET/products/{id}/scansScan-Zusammenfassung je Produkt: total, unique, byCountry, byDevice. Parameter: days=7|30|90|365.
GET/products/{id}/batterypassDer Batteriepass in CEN-CENELEC-JTC24-Form (BatteryPass). Parameter: profile=feb27|full — feb27 ist der ab 18.02.2027 verbindliche engere Satz, full das vollständige Datenmodell. Nur für die Kategorie battery.
GET/scansKontoweite Scan-Zusammenfassung: das Obige + byCategory, byLevel.

Was müssen Sie angeben? (Pflicht vs. optional)

Jede Kategorie hat einen Pflichtkern — ohne ihn weist die Schemavalidierung die Anfrage ab (422). Alle übrigen Felder sind optional, Sie können sie aber jederzeit im selben data-Objekt mitschicken (mehr Daten = besserer Produktpass und zukunftssicherere Compliance). Die genaue, maschinenlesbare Feldliste liefert /schema/{category} — mit required: true/false, Typ, Enum und localized-Kennzeichnung.

curl https://veridyn.eu/api/v1/schema/textile \
  -H "Authorization: Bearer vk_<kulcs>"

Alle Felder nach Kategorie

✓ = Pflicht · — = optional · ↳ verschachteltes Unterfeld · 🌐 je Sprache (localized) · 🔒 nur auf Behördenebene sichtbar. Diese Liste wird aus dem Schema generiert — immer aktuell.

textile Textilien & Bekleidung20 Feld

FeldTypPfl.Beschreibung
productNamestring✓Produktname — Der Produktname, wie ihn der Käufer im Produktpass sehen wird.
skustring✓SKU — Interne Artikelnummer / Kennung — aus Ihrem System.
brandNamestring✓Marke
gtinstring—GTIN (optional) — Die Barcode-Nummer (GTIN-8/12/13/14). Die Prüfziffer wird vom System validiert. Wenn nicht vorhanden, leer lassen.
commodityCodestring—Warennummer (KN/HS, optional) — Zoll-/Tarifeinreihung: Kombinierte Nomenklatur (CN, 8 Stellen) oder HS-Code. Pflicht-Metadatum des EU-DPP-Registers bei der Registrierung — steht auf den Liefer-/Zollpapieren.
economicOperatorobject✓Wirtschaftsakteur
  ↳ rolestring (enum)✓Rolle — Wer das Produkt auf dem EU-Markt in Verkehr bringt und dafür haftet. Bei den meisten Herstellern: Hersteller. (manufacturer · importer · authorized_representative · distributor · dealer · fulfilment_service_provider)
  ↳ legalNamestring✓Rechtlicher Name
  ↳ addressstring✓Adresse
  ↳ countrystring✓Land (ISO 3166-1 alpha-2) — Zweistelliger ISO-Ländercode, in Großbuchstaben — z. B. HU, DE, IT
  ↳ contactEmailstring✓Kontakt-E-Mail — Hierher können sich Käufer oder Behörden wenden.
  ↳ operatorId 🔒string—Eindeutige Akteurs-ID (GS1 GLN) — 13-stellige GS1 GLN — die eindeutige EU-DPP-Kennung des Wirtschaftsakteurs. Nur auf der Zugriffsstufe „Behörden" sichtbar. Leer lassen, wenn keine vorhanden ist.
  ↳ eori 🔒string—EORI-Nummer — EORI-Nummer des Wirtschaftsakteurs (Zoll- / EU-DPP-Registrierungskennung) — das Register identifiziert Sie darüber. Nur auf Behörden-Zugriffsebene sichtbar. Wenn nicht vorhanden, leer lassen.
languagesarray[string]✓Sprachen (BCP-47) — In welchen Sprachen der Produktpass erscheinen soll. Die lokalisierten Felder (Pflege, Lebenszyklus) müssen in jeder hier aufgeführten Sprache ausgefüllt werden. z. B. hu, en
fiberCompositionarray[object]✓Faserzusammensetzung — Woraus das Produkt besteht. Die Prozentwerte müssen genau 100 ergeben. z. B. Baumwolle 95 + Elasthan 5
  ↳ fiberstring✓Faser
  ↳ percentagenumber✓Prozentsatz
recycledContentPercentagenumber—Rezyklatanteil — Rezyklatanteil im Produkt. Wenn nicht relevant, leer lassen.
countryOfManufacturestring✓Herstellungsland (ISO) — Wo das Endprodukt hergestellt wurde. Zweistelliger ISO-Code, z. B. PT, TR, HU
supplyChainStagesarray[object]—Stufen der Lieferkette — Wo welche Fertigungsstufe stattfand (Spinnen, Weben, Färben, Konfektion…). Optional, schafft aber Vertrauen. Stufe und Land sind öffentlich; Name und ID der Anlage sind geschützt — nur für Parteien mit berechtigtem Interesse (und Behörden) über Token-Link sichtbar.
  ↳ stagestring (enum)✓Stufe (spinning · weaving · knitting · dyeing · finishing · assembly)
  ↳ countrystring✓Land (ISO)
  ↳ facilityName 🔒string—Name der Anlage
  ↳ facilityId 🔒string—Eindeutige Anlagen-ID (GS1 GLN)
careInstructions 🌐object✓Pflegehinweise — Waschen, Trocknen, Bügeln — je Sprache ein Text.
repairobject✓Reparierbarkeit — Ob und wie reparierbar. Die Felder „Reparierbar“ und „Ersatzteile verfügbar“ sind Pflichtfelder.
  ↳ repairableboolean✓Reparierbar
  ↳ instructions 🌐object—Reparaturhinweise — Wie es repariert werden kann — je Sprache (optional).
  ↳ sparePartsAvailableboolean✓Ersatzteile verfügbar (z. B. Knopf, Reißverschluss)
substancesOfConcernarray[object]—Besorgniserregende Stoffe — Besorgniserregende Stoffe (z. B. REACH SVHC), falls im Produkt enthalten. Bei den meisten Produkten kann das Feld leer bleiben.
  ↳ namestring✓Name
  ↳ casNumberstring—CAS-Nummer
  ↳ concentrationRangestring—Konzentrationsbereich
durabilityobject—Haltbarkeit — Haltbarkeitsdaten, falls Ihnen Messwerte vorliegen (optional).
  ↳ testResultsstring—Testergebnisse
  ↳ pefScorenumber—PEF-Wert — Product Environmental Footprint-Wert, falls vorhanden. (optional)
carbonFootprintnumber—CO₂-Fußabdruck (kg CO₂e) — Gesamter CO₂-Fußabdruck des Produkts in kg CO₂-Äquivalent. Im Thema „Impact“ erscheint er als Vergleich „≈ km mit dem Auto“.
waterFootprintnumber—Wasser-Fußabdruck (Liter) — Für die Herstellung verbrauchte Wassermenge in Litern. Optional.
weightGramsnumber—Gewicht (Gramm) — Gewicht des Produkts in Gramm. Optional.
endOfLifeobject✓Lebensende — Was mit dem Produkt nach der Nutzung geschehen soll — je Sprache auszufüllen.
  ↳ recyclingInstructions 🌐object✓Recyclinghinweise — Wie es recycelt werden kann — je Sprache.
  ↳ disposalInstructions 🌐object✓Entsorgungshinweise — Falls nicht recycelbar — je Sprache.
complianceDocumentsarray[object]—Konformitätsdokumente — Links zu Zertifikaten und Konformitätsdokumenten (optional). z. B. OEKO-TEX, GOTS.
  ↳ typestring✓Typ
  ↳ urlstring✓URL

battery Batterie105 Feld

FeldTypPfl.Beschreibung
productNamestring✓Produktname — Der Name der Batterie, wie ihn der Käufer im Produktpass sehen wird.
skustring✓SKU — Interne Artikelnummer / Kennung — aus Ihrem System.
brandNamestring✓Marke
gtinstring—GTIN (optional) — Die Barcode-Nummer (GTIN-8/12/13/14). Die Prüfziffer wird vom System validiert. Wenn nicht vorhanden, leer lassen.
commodityCodestring—Warennummer (KN/HS, optional) — Zoll-/Tarifeinreihung: Kombinierte Nomenklatur (CN, 8 Stellen) oder HS-Code. Pflicht-Metadatum des EU-DPP-Registers bei der Registrierung — steht auf den Liefer-/Zollpapieren.
economicOperatorobject✓Wirtschaftsakteur
  ↳ rolestring (enum)✓Rolle — Wer das Produkt auf dem EU-Markt in Verkehr bringt und dafür haftet. Bei den meisten Herstellern: Hersteller. (manufacturer · importer · authorized_representative · distributor · dealer · fulfilment_service_provider)
  ↳ legalNamestring✓Rechtlicher Name
  ↳ addressstring✓Adresse
  ↳ countrystring✓Land (ISO 3166-1 alpha-2) — Zweistelliger ISO-Ländercode, in Großbuchstaben — z. B. HU, DE, IT
  ↳ contactEmailstring✓Kontakt-E-Mail — Hierher können sich Käufer oder Behörden wenden.
  ↳ operatorId 🔒string—Eindeutige Akteurs-ID (GS1 GLN) — 13-stellige GS1 GLN — die eindeutige EU-DPP-Kennung des Wirtschaftsakteurs. Leer lassen, wenn keine vorhanden ist.
  ↳ eori 🔒string—EORI-Nummer — EORI-Nummer des Wirtschaftsakteurs (Zoll- / EU-DPP-Registrierungskennung) — das Register identifiziert Sie darüber. Nur auf Behörden-Zugriffsebene sichtbar. Wenn nicht vorhanden, leer lassen.
languagesarray[string]✓Sprachen (BCP-47) — In welchen Sprachen der Produktpass erscheinen soll. Die lokalisierten Felder (Sicherheit, Lebenszyklus) müssen in jeder hier aufgeführten Sprache ausgefüllt werden. z. B. hu, en
batteryCategorystring (enum)✓Batteriekategorie — Kategorie gemäß (EU) 2023/1542. Der Produktpass ist zuerst für EV-, Industrie- (> 2 kWh) und LMT-Batterien verpflichtend (18. Februar 2027). (portable · lmt · ev · industrial · sli)
cellChemistrystring (enum)✓Zellchemie — Zellchemie-Typ der Batterie. Die Liste folgt der Codeliste von CEN-CENELEC JTC24; „LTO" und „Sonstige" stehen nicht in der offiziellen Liste, damit kann die BatteryPass-Registrierung scheitern. (nmc · nca · lfp · lmo · lto · nimh · lead_acid · sodium_ion …)
weightKgnumber✓Gewicht (kg) — Gewicht der Batterie in Kilogramm.
countryOfManufacturestring✓Herstellungsland (ISO) — Wo die Batterie hergestellt wurde. Zweistelliger ISO-Code, z. B. DE, HU, CN
manufacturingDatestring✓Herstellungsdatum — Genaues Herstellungsdatum im ISO-Format (JJJJ-MM-TT). Das JTC24-Datenmodell erwartet ein vollständiges Datum — mit Jahr oder Jahr-Monat scheitert die BatteryPass-Registrierung.
ratedCapacitynumber✓Nennkapazität (Ah) — Nennkapazität in Amperestunden (Ah).
energyWhnumber—Energie (Wh) — Gesamtenergieinhalt in Wattstunden (Wh). Optional.
nominalVoltagenumber—Nennspannung (V) — Nennspannung in Volt. Optional.
expectedLifetimeCyclesnumber—Erwartete Lebensdauer (Ladezyklen) — Anzahl der garantierten / erwarteten Ladezyklen insgesamt. BatteryPass erwartet eine ganze Zahl, daher erscheint ein gebrochener Wert im JTC24-Export gerundet. Optional.
stateOfHealth 🔒number—Gesundheitszustand — Gesundheitszustand der Batterie in % der ursprünglichen Kapazität (bei neuer Batterie 100). STÜCK-SPEZIFISCH und dynamisch — gehört idealerweise auf die Einheits-Ebene (nicht in das Modell-Template). Optional.
remainingCapacity 🔒number—Verbleibende Kapazität (Ah) — Aktuelle Kapazität des Stücks — anders als die Nennkapazität nimmt sie mit der Zeit ab. Das LMT-Profil schreibt sie VOR, das EV-Profil nicht.
remainingPowerAt80Soc 🔒number—Verbleibende Leistungsfähigkeit bei 80 % SoC (W) — Gegenstück zur ursprünglichen Leistung: der AKTUELLE Wert bei 80 % Ladezustand.
remainingPowerAt20Soc 🔒number—Verbleibende Leistungsfähigkeit bei 20 % SoC (W) — Dasselbe bei 20 % Ladezustand. JTC24 verlangt beide Werte zusammen.
minimumVoltagenumber—Mindestspannung (V) — Kleinste Spannung, bei der die Batterie noch arbeitet.
maximumVoltagenumber—Maximalspannung (V) — Höchste zulässige Betriebsspannung.
originalPowerAt80Socnumber—Ursprüngliche Leistungsfähigkeit bei 80 % SoC (W) — JTC24 verlangt die ursprüngliche Leistung bei ZWEI Ladezuständen; dies ist der Wert bei 80 %.
originalPowerAt20Socnumber—Ursprüngliche Leistungsfähigkeit bei 20 % SoC (W) — Die ursprüngliche Leistung bei 20 % Ladezustand. Zusammen mit dem 80-%-Wert in JTC24 verpflichtend.
maximumPermittedBatteryPowernumber—Maximal zulässige Batterieleistung (W) — Vom Hersteller zugelassene Spitzenleistung.
initialRoundTripEfficiencynumber—Anfängliche Energieeffizienz im Lade-/Entladezyklus (%) — Verhältnis von zurückgewonnener zu eingespeister Energie im Neuzustand.
roundTripEfficiencyAt50PctCycleLifenumber—Energieeffizienz im Lade-/Entladezyklus bei 50 % der Zyklenlebensdauer (%) — Derselbe Wirkungsgrad bei der Hälfte der Nennzyklenzahl — daran zeigt sich das Tempo der Alterung.
initialInternalResistanceCellnumber—Anfänglicher Innenwiderstand — Zelle (Ω) — JTC24 verlangt den Innenwiderstand getrennt auf ZELL- und PACK-Ebene; dies ist der Zellwert.
initialInternalResistancePacknumber—Anfänglicher Innenwiderstand — Batteriepack (Ω) — Anfänglicher Innenwiderstand des gesamten Batteriepacks.
cycleLifeReferenceTeststring—Referenztest für die Zyklenlebensdauer — Nach welcher Norm der Test durchgeführt wurde, der die Zyklenzahl ergab.
cRateOfCycleLifeTestnumber—C-Rate des Zyklenlebensdauer-Tests (A/Ah) — Lade-/Entladestrom des Tests im Verhältnis zur Kapazität. Erwartet wird eine ZAHL: 1C entspricht hier 1.
capacityThresholdForExhaustionnumber—Kapazitätsschwelle für die Erschöpfung (%) — Unterhalb welchen Prozentsatzes der ursprünglichen Kapazität die Batterie als erschöpft gilt.
temperatureRangeIdleLowernumber—Temperaturbereich im Ruhezustand — untere Grenze (°C) — Niedrigste Temperatur, die die Batterie außer Betrieb verträgt. Kann negativ sein.
temperatureRangeIdleUppernumber—Temperaturbereich im Ruhezustand — obere Grenze (°C) — Höchste Temperatur, die die Batterie außer Betrieb verträgt.
stateOfChargeSoC 🔒number—Ladezustand — SoC (%) — Der momentane Ladezustand. STÜCKBEZOGEN und laufend veränderlich.
stateOfCertifiedEnergy 🔒number—Zustand der zertifizierten Energie — SOCE (%) — Die noch verfügbare Energie im Verhältnis zur zertifizierten Energie.
capacityFade 🔒number—Kapazitätsverlust (%) — Wie viel die Batterie von ihrer ursprünglichen Kapazität verloren hat.
powerFade 🔒number—Leistungsverlust (%) — Wie viel die Batterie von ihrer ursprünglichen Leistung verloren hat.
roundTripEfficiencyFade 🔒number—Rückgang der Lade-/Entladeeffizienz (%) — Verschlechterung des Round-Trip-Wirkungsgrads gegenüber dem Ausgangswert.
roundTripEfficiencyNow 🔒number—Round-Trip-Wirkungsgrad — aktuell (%) — Der AKTUELLE Round-Trip-Wirkungsgrad des Exemplars. Die Leitlinie der Kommission (v2.0) verlangt den Momentanwert, nicht nur den Rückgang; der Rückgang lässt sich daraus und aus dem Anfangswert berechnen. JTC24 kennt dieses Feld nicht, daher gibt es keinen Standardpfad.
internalResistanceIncreasePack 🔒number—Anstieg des Innenwiderstands — Batteriepack (%) — Um wie viel der Innenwiderstand des Packs gegenüber dem Anfangswert gestiegen ist. JTC24 verlangt hier nur den PACK-Wert.
internalResistanceCell 🔒number—Innenwiderstand — Zelle, aktuell (Ω) — Der AKTUELLE Innenwiderstand der Zelle. Die Leitlinie der Kommission (v2.0) verlangt den momentanen Ohm-Wert; der Anstieg lässt sich daraus und aus dem Anfangswert berechnen. JTC24 kennt nur den Anfangswert und den Anstieg, daher gibt es dafür keinen Standardpfad.
internalResistanceModule 🔒number—Innenwiderstand — Modul, aktuell (Ω) — Der AKTUELLE Innenwiderstand des Moduls. Die Leitlinie der Kommission (v2.0) verlangt den momentanen Ohm-Wert; der Anstieg lässt sich daraus und aus dem Anfangswert berechnen. JTC24 kennt nur den Anfangswert und den Anstieg, daher gibt es dafür keinen Standardpfad.
internalResistancePack 🔒number—Innenwiderstand — Pack, aktuell (Ω) — Der AKTUELLE Innenwiderstand des Packs. Die Leitlinie der Kommission (v2.0) verlangt den momentanen Ohm-Wert; der Anstieg lässt sich daraus und aus dem Anfangswert berechnen. JTC24 kennt nur den Anfangswert und den Anstieg, daher gibt es dafür keinen Standardpfad.
selfDischargeRate 🔒number—Selbstentladungsrate (%/Monat) — Wie viel Ladung das Exemplar außer Betrieb verliert, pro Monat. Die Leitlinie der Kommission (v2.0) verlangt bei LMT-Batterien die ENTWICKLUNG der Selbstentladung; die Entwicklung ergibt sich aus dem regelmäßig aktualisierten Wert zusammen mit der Versionshistorie des Passes. Das JTC24-EV-Profil kennt dieses Feld nicht.
fullCycleCount 🔒integer—Anzahl vollständiger Lade-/Entladezyklen — Wie viele vollständige Zyklen die Batterie bisher durchlaufen hat. STÜCKBEZOGEN.
expectedLifetimeYears 🔒number—Erwartete Lebensdauer (Kalenderjahre) — Erwartete Lebensdauer in Jahren — neben der Zyklenzahl die zweite Lebensdauer-Kennzahl.
temperatureInformation 🔒number—Temperaturangaben (°C) — JTC24 erwartet hier einen einzelnen °C-Wert (in der Regel die mittlere Betriebs- oder Referenztemperatur).
accidentInfoUrl 🔒string—Informationen zu Unfällen (Link) — Verweis auf die Dokumentation von Unfällen bzw. außergewöhnlichen Ereignissen mit der Batterie.
carbonFootprintnumber—CO₂-Fußabdruck (kg CO₂e / kWh) — CO₂-Fußabdruck der Batterie über den gesamten Lebenszyklus, in kg CO₂-Äquivalent pro kWh Gesamtenergie.
carbonFootprintClassstring—CO₂-Fußabdruck-Klasse (A–G) — CF-Leistungsklasse gemäß Verordnung, sofern verfügbar. Optional.
carbonFootprintStudyUrlstring—CO₂-Fußabdruck-Studie (URL) — Link zur Studie / Dokumentation, auf der die CO₂-Fußabdruck-Berechnung beruht (gemäß delegiertem CF-Rechtsakt). Optional.
carbonFootprintBreakdownarray[object]—CO₂-Fußabdruck nach Lebenszyklusphase — Aufschlüsselung des CO₂-Fußabdrucks nach Lebenszyklusphasen (kg CO₂e / kWh) — so vom delegierten CF-Rechtsakt erwartet. Optional.
  ↳ stagestring (enum)✓Lebenszyklusphase (raw_material · main_production · distribution · recycling)
  ↳ valuenumber✓Wert (kg CO₂e / kWh)
recycledContentarray[object]—Anteil recycelter Rohstoffe — Anteil recycelter kritischer Rohstoffe je Material (Kobalt, Lithium, Nickel, Blei). Optional, wird von der Verordnung aber zunehmend strenger erwartet.
  ↳ materialstring (enum)✓Material (cobalt · lithium · nickel · lead)
  ↳ percentagenumber✓Recyclinganteil
hazardousSubstancesarray[object]—Gefahrstoffe — In der Batterie enthaltene Gefahrstoffe (über Quecksilber, Cadmium, Blei hinaus). Bei den meisten Datenblättern relevant.
  ↳ namestring✓Name
  ↳ casNumberstring—CAS-Nummer
  ↳ concentrationRangestring—Konzentrationsbereich
  ↳ concentrationnumber—Konzentration (%) — Die Konzentration als ZAHL. Der obige Bereich („< 20 %") ist für Menschen gedacht, JTC24 erwartet jedoch eine Zahl — deshalb werden beide benötigt.
  ↳ hazardPhrasesarray[string]—Gefahrenhinweise (H-Sätze) — Codes der H-Sätze nach der CLP-Verordnung, einzeln.
  ↳ precautionaryPhrasesarray[string]—Sicherheitshinweise (P-Sätze) — Codes der P-Sätze nach der CLP-Verordnung, einzeln.
  ↳ substanceLocationstring—Ort des Stoffes — In welchem Teil der Batterie der Stoff enthalten ist.
criticalRawMaterialsarray[string]—Kritische Rohstoffe — NAMEN der in der Batterie enthaltenen kritischen Rohstoffe, einzeln. Etwas anderes als der Rezyklatanteil: dort geht es um den Anteil, hier nur um das Vorhandensein.
cathodeAnodeElectrolyteMaterials 🔒array[string]—Kathoden-, Anoden- und Elektrolytmaterialien — In den Hauptbestandteilen der Zelle verwendete Materialien, einzeln aufgeführt.
safetyInformation 🌐object—Sicherheitshinweise — Hinweise zu Handhabung, Lagerung und Notfällen — je Sprache ein Text.
dueDiligenceUrlstring—Sorgfaltspflichtenbericht der Lieferkette (URL) — Link zur Sorgfaltspflichten-Strategie bzw. zum Bericht nach der Verordnung. BEWUSST ÖFFENTLICH: Die Batterieverordnung schreibt die Veröffentlichung des Berichts vor, und auch JTC24 stuft ihn als `public` ein. (Artikel 77 Absatz 4 betrifft die übrigen, eingeschränkten Felder — dieses nicht.) Optional.
endOfLifeobject✓Lebensende — Sammlung, Recycling, Demontage — je Sprache auszufüllen.
  ↳ recyclingInstructions 🌐object✓Sammlung / Recycling — Wo es zurückgegeben und wie es recycelt werden kann — je Sprache.
  ↳ disposalInstructions 🌐object✓Entsorgung / Warnhinweise — Verbote und Gefahren — je Sprache.
complianceDocumentsarray[object]—Konformitätsdokumente — Links zu Zertifikaten, Konformitäts- und Prüfdokumenten (optional). z. B. CE, UN 38.3.
  ↳ typestring✓Typ
  ↳ urlstring✓URL
batterySerialNumberstring—Seriennummer der Batterie — Seriennummer des KONKRETEN Stücks. Der Batteriepass ist stückbezogen, daher ist dieses Feld nur je Einheit sinnvoll auszufüllen, nicht je Modell.
uniqueManufacturerIdentifier 🔒string—Eindeutige Herstellerkennung — Eindeutige Kennung des Herstellers (z. B. GLN oder EORI). JTC24 stuft sie auf BEHÖRDEN-Ebene ein.
thirdPartyServiceProvider 🔒string—Dritter Dienstleister — Dienstleister, der den Pass im Namen des Wirtschaftsakteurs verwaltet. BEHÖRDEN-Ebene.
batteryStatus 🔒string (enum)—Status der Batterie — Lebenszyklus-Status des Stücks. Folgt der Codeliste von JTC24; STÜCKBEZOGEN. (original · re-used · remanufactured · repurposed · waste)
manufacturerInformationobject—Angaben zum Hersteller — Angaben zum HERSTELLER — dieser ist nicht zwingend mit dem in Verkehr bringenden Wirtschaftsakteur identisch, deshalb verlangt JTC24 beide getrennt.
  ↳ namestring✓Rechtlicher Name
  ↳ tradeNamestring✓Eingetragener Handelsname / eingetragene Marke
  ↳ postalAddressstring✓Postanschrift
  ↳ webAddressstring—Internetadresse
  ↳ eMailAddressstring—E-Mail
dismantlingInformation 🔒string—Demontageinformationen (Link) — Wie die Batterie sicher zu demontieren ist. Richtet sich an Reparaturbetriebe und Entsorger.
partNumbersForComponents 🔒string—Teilenummern der Komponenten (Link) — Liste der Teilenummern der austauschbaren Bauteile.
informationOnSourcesOfSpareParts 🔒string—Bezugsquellen für Ersatzteile (Link) — Wo Ersatzteile bezogen werden können.
safetyMeasuresUrl 🔒string—Sicherheitsmaßnahmen (Link) — Dokument mit den bei Demontage und Handhabung einzuhaltenden Vorsichtsmaßnahmen.
renewableContentSharenumber—Anteil erneuerbarer Materialien (%) — Anteil der aus erneuerbaren Quellen stammenden Materialien in der Batterie.
wastePreventionInfoUrlstring—Abfallvermeidung — Informationen für Endnutzer (Link) — Was die Nutzerin oder der Nutzer zur Abfallvermeidung tun kann. ÖFFENTLICHE Information.
separateCollectionInfoUrlstring—Getrenntsammlung — Informationen für Endnutzer (Link) — Wie die Nutzerin oder der Nutzer die Altbatterie getrennt entsorgt.
secondLifeTreatmentInfoUrlstring—Zweitverwendung und Behandlung am Lebensende (Link) — Beschreibung von Sammlung, Vorbereitung zur Wiederverwendung und Behandlung am Lebensende.
separateCollectionSymbolUrlstring—Symbol für die Getrenntsammlung (Link) — Abbildung oder Beschreibung des Symbols der durchgestrichenen Abfalltonne.
carbonFootprintLabelUrlstring—CO₂-Fußabdruck-Kennzeichnung (Link) — Kennzeichen der CO₂-Fußabdruck-Leistungsklasse.
euDeclarationOfConformityUrlstring—EU-Konformitätserklärung (Link) — Fundstelle der EU-Konformitätserklärung. Von der Verordnung zwingend verlangt.
testReportsUrl 🔒string—Prüfberichte zum Nachweis der Konformität (Link) — Prüfberichte zum Nachweis der Konformität. Nach JTC24 auf BEHÖRDEN-Ebene.
meaningOfLabelsAndSymbols 🌐object—Bedeutung der Kennzeichnungen und Symbole — Erläuterung der auf der Batterie angebrachten Kennzeichen für die Nutzerin oder den Nutzer.
extinguishingAgentstring—Löschmittel — Mit welchem Löschmittel ein Batteriebrand gelöscht werden kann. Für die Feuerwehr eine kritische Angabe.
extinguishingAgentFireClassstring—Brandklasse — Brandklasse des Löschmittels (A/B/C/D/F).
industrialSubtypestring (enum)—Untertyp der Industriebatterie — Die Verordnung kennt eine einzige Kategorie „Industriebatterie", das JTC24-Datenmodell vergibt dafür jedoch drei getrennte Codes. Dieses Feld entscheidet, mit welchem der Batteriepass exportiert wird. Nur bei der Kategorie Industriebatterie relevant. (without_bms · stationary_above_2kwh · non_stationary_above_2kwh)
economicOperatorWebstring—Website des Wirtschaftsakteurs — Website des in Verkehr bringenden Akteurs. JTC24 verlangt sie bei den Akteursdaten.
facilityId 🔒string—Kennung der Betriebsstätte — Kennung des Standorts, an dem die Batterie hergestellt wurde — GLN oder eigene Kennung.
warrantyPeriodstring—Garantiedauer — ISO-8601-Dauer: P8Y = 8 Jahre, P8Y6M = 8 Jahre 6 Monate. JTC24 erwartet dieses Format.
dateOfPuttingIntoServicestring—Datum der Inbetriebnahme — Wann dieses Stück in Betrieb genommen wurde. Stückbezogene Angabe: für das Modell nicht sinnvoll.
cellChemistryOtherstring—Zellchemie — sonstige, näher bezeichnet — Ist die Zellchemie „Sonstige", geben Sie hier die genaue Bezeichnung an. JTC24 hat dafür ein eigenes Feld.
certifiedUsableEnergyKwhnumber—Zertifizierte nutzbare Batterieenergie (kWh) — Im Zertifizierungsverfahren gemessene nutzbare Energie. Darauf bezieht sich der SOCE-Wert.
initialInternalResistanceModulenumber—Anfänglicher Innenwiderstand — Modul (Ω) — Neben dem Zell- und dem Packwert kennt JTC24 auch den Wert auf Modulebene.
measurementTempLowerinteger—Messtemperatur — untere Grenze (°C) — In welchem Temperaturbereich die obigen Spannungs- und Leistungsangaben gelten. JTC24 verlangt ihn für alle sechs Gruppen getrennt; hier geben Sie ihn einmal an.
measurementTempUpperinteger—Messtemperatur — obere Grenze (°C) — Obere Grenze des Gültigkeitsbereichs der obigen Angaben.
remainingUsableEnergyKwh 🔒number—Verbleibende nutzbare Batterieenergie (kWh) — Die derzeit entnehmbare Energie des Stücks. Gegenüber dem zertifizierten Wert nimmt sie ab.
overchargeEventCount 🔒integer—Anzahl der Überladungsereignisse — Wie oft die Batterie überladen wurde. Eine Eingangsgröße der Lebensdauerschätzung.
deepDischargeEventCount 🔒integer—Anzahl der Tiefentladungsereignisse — Wie oft die Batterie bis zur Tiefentladung entladen wurde.
internalResistanceIncreaseCell 🔒number—Anstieg des Innenwiderstands — Zelle (%) — Um wie viel der Innenwiderstand auf Zellebene gegenüber dem Ausgangswert gestiegen ist. Neben dem Packwert kennt JTC24 auch diesen.
internalResistanceIncreaseModule 🔒number—Anstieg des Innenwiderstands — Modul (%) — Um wie viel der Innenwiderstand auf Modulebene gegenüber dem Ausgangswert gestiegen ist. Neben dem Packwert kennt JTC24 auch diesen.
carbonFootprintAbsoluteinteger—CO₂-Fußabdruck — absolut (kg CO₂e) — CO₂-Fußabdruck des gesamten Lebenszyklus als Absolutwert. Neben dem auf kWh bezogenen Wert verlangt JTC24 auch diesen.
supplyChainIndicesstring—Lieferketten-Indizes — Risikoindikatoren der Lieferkette, wie in der Sorgfaltspflichtenregelung angegeben.
thirdPartyAssurancesstring—Prüfungen Dritter nach anerkannten Systemen — Welches anerkannte System (z. B. IRMA, RMI) die Sorgfaltsprüfung bestätigt hat.
symbolsForCadmiumAndLeadUrlstring—Cadmium-/Blei-Symbol (Bild oder Dokument) — Kennzeichnung mit Warnhinweis auf Cd/Pb-Gehalt. Der Fortschritt verlangt sie nur bei Nickel-Cadmium- und Blei-Säure-Chemie; enthält eine andere Chemie mehr als 0,002 % Cadmium oder 0,004 % Blei, füllen Sie das Feld ebenfalls aus.
modulesIndividuallyReplaceable 🔒string (enum)—Zellmodule einzeln austauschbar — Lässt sich EIN defektes Modul austauschen, ohne das gesamte Pack zu ersetzen? (yes · partly · no)
cellsIndividuallyReplaceable 🔒string (enum)—Zellen einzeln austauschbar — Lässt sich das Modul bis auf Zellebene öffnen, oder ist es eine geschlossene Einheit? (yes · partly · no)
bmsSeparatelyRemovable 🔒string (enum)—BMS separat entnehmbar — Ist die Elektronik zugänglich, ohne die Zellen zu öffnen? (yes · partly · no)
professionalServiceRequired 🔒string (enum)—Fachwerkstatt und Spezialwerkzeug erforderlich — Lässt sich das Produkt mit handelsüblichem Werkzeug öffnen, oder ist eine Fachkraft nötig? (yes · partly · no)
sparePartsAvailabilityYears 🔒integer—Verfügbarkeit von Ersatzteilen (Jahre) — Wie viele Jahre ab Inverkehrbringen sichert der Hersteller die Ersatzteilversorgung zu?

furniture Möbel20 Feld

FeldTypPfl.Beschreibung
productNamestring✓Produktname — Der Produktname, wie ihn der Käufer im Produktpass sehen wird.
skustring✓SKU — Interne Artikelnummer / Kennung — aus Ihrem System.
brandNamestring✓Marke
gtinstring—GTIN (optional) — Die Barcode-Nummer (GTIN-8/12/13/14). Die Prüfziffer wird vom System validiert. Wenn nicht vorhanden, leer lassen.
commodityCodestring—Warennummer (KN/HS, optional) — Zoll-/Tarifeinreihung: Kombinierte Nomenklatur (CN, 8 Stellen) oder HS-Code. Pflicht-Metadatum des EU-DPP-Registers bei der Registrierung — steht auf den Liefer-/Zollpapieren.
economicOperatorobject✓Wirtschaftsakteur
  ↳ rolestring (enum)✓Rolle — Wer das Produkt auf dem EU-Markt in Verkehr bringt und dafür haftet. Bei den meisten Herstellern: Hersteller. (manufacturer · importer · authorized_representative · distributor · dealer · fulfilment_service_provider)
  ↳ legalNamestring✓Rechtlicher Name
  ↳ addressstring✓Adresse
  ↳ countrystring✓Land (ISO 3166-1 alpha-2) — Zweistelliger ISO-Ländercode, in Großbuchstaben — z. B. HU, DE, IT
  ↳ contactEmailstring✓Kontakt-E-Mail — Hierher können sich Käufer oder Behörden wenden.
  ↳ operatorId 🔒string—Eindeutige Akteurs-ID (GS1 GLN) — 13-stellige GS1 GLN — die eindeutige EU-DPP-Kennung des Wirtschaftsakteurs. Nur auf der Zugriffsstufe „Behörden" sichtbar. Leer lassen, wenn keine vorhanden ist.
  ↳ eori 🔒string—EORI-Nummer — EORI-Nummer des Wirtschaftsakteurs (Zoll- / EU-DPP-Registrierungskennung) — das Register identifiziert Sie darüber. Nur auf Behörden-Zugriffsebene sichtbar. Wenn nicht vorhanden, leer lassen.
languagesarray[string]✓Sprachen (BCP-47) — In welchen Sprachen der Produktpass erscheinen soll. Die lokalisierten Felder (Pflege, Montage, Lebenszyklus) müssen in jeder hier aufgeführten Sprache ausgefüllt werden. z. B. hu, en
materialCompositionarray[object]✓Materialzusammensetzung — Woraus das Möbelstück besteht. Die Prozentwerte müssen genau 100 ergeben. z. B. Eiche massiv 80 + Metall 20
  ↳ materialstring (enum)✓Material (wood · engineeredWood · metal · plastic · glass · textile · foam · leather …)
  ↳ percentagenumber✓Prozentsatz
woodCertificationobject—Holzherkunft & Zertifizierung — Nachhaltige Herkunft und Zertifizierung des verwendeten Holzes, sofern relevant (optional).
  ↳ schemestring (enum)—Zertifizierungssystem — Zertifizierung des Holzes für nachhaltige Waldbewirtschaftung. (fsc · pefc · none)
  ↳ countrystring—Ursprungsland (ISO) — Ursprungsland des Holzes. Zweistelliger ISO-Code, z. B. AT, SE, RO. Optional.
dimensionsobject—Abmessungen & Gewicht — Außenmaße und Gewicht des Möbelstücks (optional).
  ↳ widthnumber—Breite (cm)
  ↳ depthnumber—Tiefe (cm)
  ↳ heightnumber—Höhe (cm)
  ↳ weightKgnumber—Gewicht (kg)
countryOfManufacturestring✓Herstellungsland (ISO) — Wo das Endprodukt hergestellt wurde. Zweistelliger ISO-Code, z. B. PL, RO, HU
careInstructions 🌐object✓Pflegehinweise — Reinigung, Pflege, Oberflächenpflege — je Sprache ein Text.
assemblyInstructions 🌐object—Montageanleitung — Wie das Möbelstück montiert wird — je Sprache (optional). Ein Link ist ebenfalls möglich.
repairobject✓Reparierbarkeit — Ob und wie reparierbar. Die Felder „Reparierbar“ und „Ersatzteile verfügbar“ sind Pflichtfelder.
  ↳ repairableboolean✓Reparierbar
  ↳ instructions 🌐object—Reparaturhinweise — Wie es repariert werden kann — je Sprache (optional).
  ↳ sparePartsAvailableboolean✓Ersatzteile verfügbar (z. B. Beschläge, Bein)
  ↳ sparePartsUrlstring—Ersatzteile (URL) — Wo Ersatzteile bestellt werden können (optional).
warrantyMonthsnumber—Garantie (Monate) — Dauer der Herstellergarantie in Monaten. Optional.
substancesOfConcernarray[object]—Besorgniserregende Stoffe — Besorgniserregende Stoffe (z. B. REACH SVHC / SCIP), falls im Produkt enthalten. Bei den meisten Produkten kann das Feld leer bleiben.
  ↳ namestring✓Name
  ↳ casNumberstring—CAS-Nummer
  ↳ notestring—Hinweis
flameRetardantsstring (enum)—Flammschutzmittel — Ob das Produkt (insbesondere Polster / Schaumstoff) Flammschutzmittel enthält. Optional. (present · absent · unknown)
complianceDocumentsarray[object]—Konformitätsdokumente — Links zu Zertifikaten und Konformitätsdokumenten (optional). z. B. EN 12520, EN 1728, Brandschutzzertifikat.
  ↳ typestring✓Typ
  ↳ urlstring✓URL
packagingstring (enum)—Recyclingfähigkeit der Verpackung — Recyclingfähigkeit der Produktverpackung. Optional. (recyclable · partiallyRecyclable · notRecyclable)
endOfLifeobject✓Lebensende — Was mit dem Möbelstück nach der Nutzung geschehen soll — je Sprache auszufüllen.
  ↳ recyclingInstructions 🌐object✓Recyclinghinweise — Wie es zerlegt und recycelt werden kann — je Sprache.
  ↳ disposalInstructions 🌐object✓Entsorgungshinweise — Falls nicht recycelbar — je Sprache.

Sprachen

Das Feld languages (z. B. ["hu","en"]) deklariert, in welchen Sprachen Freitextinhalte vorliegen. Die lokalisierten Felder (Pflege, Lebensende … — im /schema mit "localized": true) müssen nur in der ersten (primären) Sprache ausgefüllt werden; die übrigen sind optional und fallen bei Fehlen auf eine verfügbare Sprache zurück. Die UI-Beschriftungen des Produktpasses werden davon unabhängig automatisch in 24 Sprachen übersetzt.

Beispiele

Massenimport aus dem ERP

curl -X POST https://veridyn.eu/api/v1/products:batch   -H "Authorization: Bearer vk_<kulcs>"   -H "Content-Type: application/json"   -H "Idempotency-Key: erp-2026-09-24-001"   -d '{
    "category": "battery",
    "on_duplicate": "skip",
    "products": [
      { "sku": "VE60-001", "productName": "PowerCell 60", "...": "..." },
      { "sku": "VE60-002", "productName": "PowerCell 60 XL", "...": "..." }
    ]
  }'

Die Antwort ist ein Bericht je Zeile: created / updated / exists (SKU bereits vorhanden) / invalid (mit den Fehlern) / plan_limit. Teilerfolg ist normal — eine fehlerhafte Zeile stoppt die übrigen nicht.

Der Header Idempotency-Key nimmt Wiederholungen das Risiko: Wird die Anfrage nach einem Timeout mit demselben Schlüssel wiederholt, kommt die frühere Antwort zurück, statt alles ein zweites Mal anzulegen.

Produkt anlegen

curl -X POST https://veridyn.eu/api/v1/products \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "textile",
    "data": {
      "productName": "Bio-Baumwoll-T-Shirt",
      "sku": "TEE-001",
      "brandName": "Lumora",
      "languages": ["hu","en"],
      "fiberComposition": [{"fiber":"pamut","percentage":100}],
      "countryOfManufacture": "PT",
      "careInstructions": {"hu":"Mosás 30 °C","en":"Wash at 30 °C"},
      "repair": {"repairable": true, "sparePartsAvailable": false},
      "endOfLife": {
        "recyclingInstructions": {"hu":"Textilgyűjtő","en":"Textile bin"},
        "disposalInstructions": {"hu":"Ne a kukába","en":"Not household waste"}
      },
      "economicOperator": {
        "role":"manufacturer","legalName":"Lumora Kft.","address":"Budapest",
        "country":"HU","contactEmail":"[email protected]"
      }
    }
  }'

Antwort (201):

{
  "data": {
    "id": "a1993ef7-7562-43bb-92ff-eb63f02dcde9",
    "category": "textile",
    "status": "active",
    "version_no": 1,
    "passport_url": "https://veridyn.eu/<fiók>/p/a1993ef7-…",
    "qr_url": "https://veridyn.eu/api/v1/products/a1993ef7-…/qr",
    "data": { "productName": "Bio-Baumwoll-T-Shirt", … }
  }
}

Auflisten

curl https://veridyn.eu/api/v1/products?limit=25 \
  -H "Authorization: Bearer vk_<kulcs>"

Ein Produkt abrufen

curl https://veridyn.eu/api/v1/products/{id} \
  -H "Authorization: Bearer vk_<kulcs>"

Aktualisierung (neue Version)

curl -X PATCH https://veridyn.eu/api/v1/products/{id} \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{"data": {"recycledContentPercentage": 30}}'

Die übergebenen Felder werden mit den vorhandenen Daten zusammengeführt (Teilaktualisierung), anschließend validiert, und es entsteht eine neue, aufbewahrte Version — die vollständige Änderungshistorie bleibt erhalten.

Chargen- und Stückpass (Serialisierung)

Sie erstellen unter einem Modell Chargen- oder Stückpässe; das Kind erbt die Daten des Elternpasses — Sie geben nur die exemplarspezifischen Felder (lot/serial) an. Auch mehrere auf einmal (items[], max. 500); die Antwort meldet Erfolg/Fehler je Zeile.

curl -X POST https://veridyn.eu/api/v1/products/{id}/children \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{"level":"item","items":[{"serial":"SN-0001"},{"serial":"SN-0002"}]}'

Die {id} kann ein Modell oder eine Charge sein. Ein POST auf eine Chargen-ID mit level:"item" legt das Stück unter dieser Charge an und übernimmt deren Lot — der GS1-Link lautet dann …/10/lot/21/serial statt …/21/serial. So entsteht die vollständige Kette Modell → Charge → Stück.

Live-Daten in großen Mengen zurückschreiben

Ein Teil der Felder auf Stückebene sind Live-Daten: SoH, Zyklenzahl und Restkapazität der Batterie kommen aus dem BMS. Das muss nicht Stück für Stück zurückgeschrieben werden — ein Aufruf aktualisiert bis zu 500 Kind-Pässe. Die Zuordnung erfolgt über serial, lot oder id, also über das, was die Fertigungssteuerung ohnehin kennt; unsere interne Kennung müssen Sie nicht speichern.

curl -X PATCH https://veridyn.eu/api/v1/products/{id}/children \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{"change_type":"change","items":[
        {"serial":"SN-0001","data":{"stateOfHealth":97,"fullCycleCount":412}},
        {"serial":"SN-0002","data":{"stateOfHealth":95,"fullCycleCount":508}}
      ]}'

Die Antwort ist ein zeilenweiser Bericht: updated · unchanged · not_found · ambiguous (die Seriennummer ist unter dem Elternpass nicht eindeutig) · validation. Aus einer unveränderten Zeile entsteht keine neue Version — eine täglich laufende Flottensynchronisation bläht die Änderungshistorie also nicht mit leeren Versionen auf. change_type gilt für das gesamte Paket: Eine BMS-Aktualisierung ist ein change (der frühere Wert war zu seiner Zeit richtig), keine correction.

💡 Bei einem Massenaufruf entsteht ein einziger zusammenfassender children.updated-Webhook, und die Zustellung wird in die Warteschlange gestellt — sie erfolgt nicht während der Anfrage. Sonst wären 500 Zeilen 500 ausgehende HTTP-Aufrufe vor Ihrer Antwort.

Scan-Analytik

Aggregierte Statistik der QR-Scans — je Produkt oder für das gesamte Konto —, aufgeschlüsselt nach Land und Gerät. Datenschutz: keine rohen IP-Adressen; eindeutige Besucher sind eine anonyme Hash-Schätzung.

curl "https://veridyn.eu/api/v1/products/{id}/scans?days=30" \
  -H "Authorization: Bearer vk_<kulcs>"
{
  "data": {
    "product_id": "a1993ef7-…",
    "range_days": 30,
    "total": 189, "unique": 142,
    "byCountry": { "HU": 142, "DE": 38, "AT": 9 },
    "byDevice":  { "mobile": 168, "tablet": 9, "desktop": 12 }
  }
}

Inkrementelle Synchronisation (nur die Änderungen)

Ein ERP kann nicht jede Nacht den gesamten Passbestand durchlesen: Je größer der Katalog, desto länger die Laufzeit. Mit dem Parameter updated_since erhalten Sie nur das zurück, was sich seit diesem Zeitpunkt geändert hat — und die Liste liefert dann alle Ebenen (Modell, Charge, Einzelstück), denn eine Fertigungssteuerung synchronisiert auch die Einzelstück-Pässe.

curl "https://veridyn.eu/api/v1/products?updated_since=2026-09-24T00:00:00Z&limit=100" \
  -H "Authorization: Bearer vk_<kulcs>"
{
  "data": [
    { "id": "a1993ef7-…", "name": "VE60 EV modul", "status": "active",
      "updated_at": "2026-09-24T08:11:32+00:00", "passport_url": "…" },
    { "id": "c7d10b42-…", "name": "VE60-000418", "status": "archived",
      "updated_at": "2026-09-24T14:02:07+00:00", "passport_url": "…" }
  ],
  "per_page": 100,
  "has_more": true,
  "next_cursor": "MjAyNi0wOS0yNCAxNDowMjowN3xjN2QxMGI0Mi0…"
}

Die Synchronisationsantwort enthält kein page und kein total — stattdessen has_more und next_cursor. Die nächste Seite fordern Sie an, indem Sie den erhaltenen next_cursor unverändert zurückgeben: ?cursor=…. Der Cursor ist opak: Entschlüsseln oder bauen Sie ihn nicht selbst — einen selbst erzeugten Wert lehnen wir mit 422 bad_cursor ab.

curl "https://veridyn.eu/api/v1/products?cursor=MjAyNi0wOS0yNCAxNDowMjowN3xjN2QxMGI0Mi0…" \
  -H "Authorization: Bearer vk_<kulcs>"

Warum ein Cursor und keine Seitenzahl

Weil die Paginierung nach Seitenzahlen still Zeilen überspringt. Die Liste ist nach Änderungszeit sortiert: Während Sie Seite 2 abfragen, wird ein bereits gesehenes Produkt aktualisiert, springt an den Anfang der Liste, und alles dahinter rutscht um eine Position nach hinten — genau jene Zeile, die auf der Seitengrenze stand, bekommen Sie nie zu sehen. Der Client glaubt dabei, er habe alles erhalten. Der Cursor zeigt auf die konkret zuletzt verarbeitete Zeile, nicht auf eine Position, daher kann das nicht passieren. (Bei Zeitstempeln in derselben Sekunde entscheidet die interne Kennung — bei einem Import betrifft das Dutzende Zeilen.)

Auch der archivierte Pass erscheint — genau darum geht es

Archivieren entfernt das Produkt nicht aus dem Synchronisationsfenster: Es kommt mit status: "archived" zurück. Das ist der „Grabstein" — daran erkennt Ihr System, dass es den Pass von seiner eigenen Liste nehmen muss. Fielen die archivierten Pässe heraus, würde der Client sie für immer als aktiv führen.

Empfohlener Ablauf: Speichern Sie den letzten next_cursor; beim nächsten Lauf starten Sie damit und blättern weiter, bis has_more falsch ist. Beim ersten (vollständigen) Laden updated_since=1970-01-01T00:00:00Z — danach geht nur noch die Änderung über die Leitung.

Zwei Dinge, mit denen Ihr Client rechnen muss

1. Die jüngste Sekunde wird zurückgehalten. Der Änderungszeitstempel hat Sekundenauflösung, daher liefert die Liste nur bereits abgeschlossene Sekunden: ein gerade gespeicherter Produktpass erscheint typischerweise 1–2 Sekunden später in der Synchronisationsliste. Das ist Absicht. Ohne diese Sperre könnte der Cursor in eine noch offene Sekunde geraten, und ein Produktpass, der innerhalb derselben Sekunde später gespeichert wird, würde nie ausgeliefert — nicht verzögert, sondern verloren, und das unbemerkt.

2. Auch eine leere Seite bringt einen Cursor. Hat sich nichts geändert, ist data leer und has_more falsch — next_cursor ist aber trotzdem nicht leer und steht weiter vorn als der vorige. Speichern Sie ihn immer: der Cursor rückt auch dann vor, wenn es keine Änderung gab.

Zertifikate und Dokumente

Die Konformitätserklärung, der Prüfbericht und das Sicherheitsdatenblatt liegen als PDF vor — und entstehen typischerweise in einem Dokumentenmanagement oder im Schriftwechsel mit dem Labor, nicht in einem Browser-Tab. Der Dateiinhalt geht daher als base64 im Anfrage-Body:

curl -X POST https://veridyn.eu/api/v1/products/{id}/documents \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{"filename":"ce-tanusitvany.pdf","tier":"public","label":"CE","content_base64":"JVBERi0xLjQK…"}'

Über den Typ entscheidet der Inhalt, nicht der Dateiname. Die Endung kann lügen — und wer bewusst damit lügt, zielt genau darauf ab —, deshalb erkennen wir den Dateityp aus den Bytes. Ist es kein PDF, PNG, JPEG oder WebP, lautet die Antwort 415 bad_type, und es wird nichts gespeichert. Die gespeicherte Datei trägt unsere Kennung als Namen; der übermittelte filename ist nur ein Anzeige-Label.

Der tier bestimmt, wer sie sehen darf: public (auch auf dem öffentlichen Pass), legitimate_interest oder authorities — dieselbe Rangfolge wie bei den Schema-Feldern. Bei einem unbekannten Wert fällt er auf die engste Stufe, public, zurück; bei einem vertraulichen Dokument sollten Sie ihn also immer angeben.

Die Größenbeschränkung beträgt 8 MB für die dekodierte Datei. Base64 macht den Anfrage-Body größer (etwa das 4/3-Fache), und überschreitet dieser das Body-Limit des Servers, kommt der Body gar nicht erst an — darauf antworten wir mit 413 payload_too_large und nennen darin auch das tatsächliche Limit des Servers, damit Sie den Fehler nicht in Ihrem eigenen Code suchen.

Das tatsächliche Maximum können Sie auch abfragen, bevor Sie etwas hochladen: GET /health (funktioniert auch ohne Schlüssel) liefert im Feld limits.document_upload_max_bytes genau jene Zahl, die von den beiden die engere ist. Es lohnt sich, sie beim Start einmal abzuholen.

curl https://veridyn.eu/api/v1/health
{
  "status": "ok",
  "limits": {
    "document_max_bytes": 8388608,
    "request_body_max_bytes": 8388608,
    "document_upload_max_bytes": 6165626,
    "bulk_max_items": 500
  }
}

Der Download liefert die Datei zurück, nicht JSON — mit ihrem eigenen Inhaltstyp; ?dl=1 als Anhang. Das DELETE entfernt sie auch von der Festplatte.

curl https://veridyn.eu/api/v1/products/{id}/documents/42?dl=1 \
  -H "Authorization: Bearer vk_<kulcs>" -o tanusitvany.pdf

Datenabfrage bei Lieferanten

Einen Teil der Passdaten kennen Sie selbst nicht: die Faserzusammensetzung des Gewebes kennt die Weberei, die Herkunft der Zellen der Zellhersteller. Sie starten eine Datenanfrage zu konkreten Feldern, und der Lieferant füllt seinen Teil über einen Link ohne Anmeldung aus — ein Konto müssen Sie ihm nicht geben. In der Oberfläche gibt es das seit langem, über die API bisher nicht: Ein Beschaffungssystem konnte so 500 Pässe hochladen, die 500 Datenanfragen aber musste es von Hand starten.

curl -X POST https://veridyn.eu/api/v1/supplier-requests \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{"product_id":"a1993ef7-…","email":"[email protected]",
       "fields":["fiberComposition","countryOfManufacture"],
       "message":"Bitte für die nächste Lieferung.","lang":"en"}'
{
  "data": {
    "id": 17, "status": "pending", "email": "[email protected]",
    "fields": ["fiberComposition", "countryOfManufacture"],
    "expires_at": "2026-10-25 09:14:02",
    "link": "https://veridyn.eu/supplier.php?token=9f3c…",
    "emailed": true
  }
}

Der link ist immer in der Antwort enthalten — auch dann, wenn wir die E-Mail versenden. Eine maschinelle Integration schreibt dem Lieferanten typischerweise aus ihrem eigenen Mailsystem (eigene Vorlage, eigener Absender, eigene Nachverfolgung); in diesem Fall schaltet send_email: false unseren Versand ab, und Sie verschicken den Link selbst. Die fields sind die Feldschlüssel des Kategorie-Schemas (GET /schema/{category}); interne Felder lassen sich nicht delegieren. Der Link läuft nach 30 Tagen ab.

Die Einreichung prüfen und einpflegen

Solange der Lieferant nichts ausgefüllt hat, steht die Datenanfrage auf pending. Nach der Einreichung auf submitted, und am Detail-Endpunkt sehen Sie, was er geschrieben hat — mit dem rohen Feldschlüssel und einem menschenlesbaren Feldnamen, damit eine Freigabe-Oberfläche das Schema nicht kennen muss. Das Einpflegen ist ein eigener Schritt: Eingegangene Daten landen nicht automatisch im Produkt.

curl https://veridyn.eu/api/v1/supplier-requests/17 \
  -H "Authorization: Bearer vk_<kulcs>"

curl -X POST https://veridyn.eu/api/v1/supplier-requests/17/merge \
  -H "Authorization: Bearer vk_<kulcs>"

Das Einpflegen überschreibt nur die ANGEFRAGTEN Felder. Der Lieferant (oder jeder, der das Token kennt) kann mehr einreichen, aber was Sie nicht angefragt haben, rührt das Produkt nicht an — wer nur zum Herstellungsland gefragt wurde, kann Ihren Markennamen nicht überschreiben.

Sind die eingepflegten Daten ungültig (422 validation), bleibt das Produkt unberührt, und die Datenanfrage wird nicht geschlossen: Sie klären es mit dem Lieferanten und versuchen es erneut. Bei Erfolg entsteht eine neue Version, und ein product.updated-Webhook geht mit der Kennzeichnung source: "supplier_merge" hinaus — so ist in Ihrem eigenen System erkennbar, dass nicht Sie geändert haben, sondern eine Einreichung.

Archivieren und Wiederherstellen

Läuft ein Produkt aus, archivieren Sie es — den Pass löschen wir aber nicht: QR-Code und Link funktionieren weiterhin, der Pass erscheint mit dem Hinweis „archiviert". Die gesetzliche Aufbewahrung verlangt das auch: Der Pass eines bereits im Markt befindlichen Stücks muss weiterleben, auch wenn Sie das Modell nicht mehr herstellen.

curl -X POST https://veridyn.eu/api/v1/products/{id}/archive \
  -H "Authorization: Bearer vk_<kulcs>"

curl -X POST https://veridyn.eu/api/v1/products/{id}/restore \
  -H "Authorization: Bearer vk_<kulcs>"

Kein Body nötig. Der Vorgang erzeugt keine neue Version: Eine Sichtbarkeitsänderung ist keine Datenänderung, und eine Version mit leerem Unterschied würde nur Rauschen in den Änderungsverlauf bringen — auch deshalb ist es ein eigener Endpunkt und kein PATCH. Ein Webhook wird ausgelöst: product.archived bzw. product.restored.

Das Kontingent wird dadurch nicht frei: Der Modellzähler des Pakets zählt auch archivierte Pässe mit — Archivieren schafft also keinen Platz für einen neuen, und Wiederherstellen verbraucht keinen.

Änderungsverlauf (Versionen)

Jeder PATCH erzeugt eine neue, aufbewahrte Version, und die Kette ist mit Hashes gesiegelt — nachträglich lässt sie sich nicht umschreiben. Der Verlauf ist auch maschinell lesbar: Die Liste liefert die Versionen, und /versions/{no} liefert, welches Feld sich geändert hat, von welchem Wert zu welchem.

curl https://veridyn.eu/api/v1/products/{id}/versions \
  -H "Authorization: Bearer vk_<kulcs>"

curl https://veridyn.eu/api/v1/products/{id}/versions/3 \
  -H "Authorization: Bearer vk_<kulcs>"

Beides sind Leseoperationen, ein Nur-Lese-Schlüssel genügt also — einem prüfenden System müssen Sie kein Schreibrecht geben. Auch der change_type ist sichtbar: correction ist die Korrektur falscher Daten (die bereits ausgegebenen Einzelstück-Pässe veralten), change dagegen eine echte Produktänderung ab einem bestimmten Datum (die Daten früherer Stücke bleiben richtig).

Zugriffslinks (Behörde, berechtigtes Interesse)

Der öffentliche Pass zeigt die öffentlichen Felder. Die Verordnung verlangt jedoch weiter gehenden Zugang für die Marktüberwachungsbehörde und für Parteien mit berechtigtem Interesse (z. B. Recycler, Reparaturbetrieb). Dafür gibt es die ablaufenden Zugriffslinks: Sie öffnen ohne Anmeldung, für einen einzigen Pass, und verstummen bei Ablauf von selbst — Sie müssen keine Konten verteilen.

curl -X POST https://veridyn.eu/api/v1/products/{id}/grants \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{"tier":"authority","label":"NKFH 2026/114","expires_in_days":30}'
{
  "data": {
    "id": 42, "tier": "authority", "label": "NKFH 2026/114",
    "status": "active", "expires_at": "2026-10-25 09:14:02",
    "use_count": 0, "last_used_at": null,
    "token": "9f3c…",
    "passport_url": "https://veridyn.eu/acme/p/a1993ef7-…?grant=9f3c…",
    "history_url":  "https://veridyn.eu/history.php?id=a1993ef7-…&grant=9f3c…"
  }
}
FeldBedeutung
tierlegitimate_interest vagy authority. Auf einer anderen Stufe kann kein Link ausgegeben werden (422 bad_tier): Die Eigentümerstufe ist Ihr eigenes Konto, sie lässt sich nicht als Link verteilen.
expires_in_daysOptional — und wenn Sie es weglassen, gilt der Link unbefristet. Eine behördliche Prüfung ist endlich, daher lohnt es sich, den Wert immer zu setzen; nach Ablauf antwortet der Link mit 410.
labelFreitext: an wen Sie den Link ausgegeben haben. Nur für Sie sichtbar — die empfangende Partei sieht ihn nicht.
history_urlNur auf der Stufe authority gefüllt: Die Behörde kann auch den vollständigen Änderungsverlauf öffnen, die Partei mit berechtigtem Interesse nicht.
use_countWie oft der Link geöffnet wurde. Ein frisch ausgegebener Link steht immer auf 0 — die Ausgabe selbst zählt nicht als Nutzung.

Der GET listet die aktiven Links des jeweiligen Passes, und DELETE /products/{id}/grants/{gid} widerruft einen sofort — das Token antwortet danach mit 410.

Schema-Version: wann Sie Ihre Integration anpassen müssen

Die Feldliste der Produktkategorien bewegt sich mit der Gesetzgebung. Bisher wies nichts darauf hin, wenn sich ein Schema geändert hatte — Integrationen gingen still kaputt. Daher liefert GET /schema/{category} jetzt eine Version und einen ETag:

curl -i https://veridyn.eu/api/v1/schema/battery \
  -H "Authorization: Bearer vk_<kulcs>"

HTTP/1.1 200 OK
ETag: "battery.1.0.02894917d482"
X-DPP-Schema-Version: 1.0
Cache-Control: public, max-age=3600

Der ETag entsteht aus dem maschinellen Vertrag: aus Feldnamen, Typen, Pflichtangaben, Enum-Werten und Zugriffsstufen. Was ihn also nicht verändert: Bezeichnung, Hilfetext, Übersetzung. So gibt es keinen Fehlalarm, nur weil die Benennung eines Feldes präziser formuliert wurde.

curl -i https://veridyn.eu/api/v1/schema/battery \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H 'If-None-Match: "battery.1.0.02894917d482"'

HTTP/1.1 304 Not Modified

Nehmen Sie den ETag in Ihr Start-Skript oder in den täglichen Lauf auf: Solange Sie eine 304 erhalten, steht der Vertrag und es gibt nichts zu tun. Kommt eine 200, zeigen die Felder version, revised und contract_hash der Antwort, was sich bewegt hat.

Steht für eine Kategorie eine nicht rückwärtskompatible Änderung bevor, trägt die Antwort zusätzlich die Header Deprecation und Sunset (RFC 8594) — Sunset ist dabei das früheste Datum, an dem die alte Form entfällt. Standardmäßig erscheint keiner der beiden Header; sehen Sie einen, haben Sie eine echte Frist.

Antwort & Fehler

Erfolg: die Nutzdaten stehen unter dem Schlüssel data. Im Fehlerfall:

{ "error": "Ungültige Daten.", "code": "validation", "errors": [ … ] }
HTTPcodeBedeutung
401unauthorizedFehlender/ungültiger API-Schlüssel.
403plan_requiredDie API ist ab dem Pro-Tarif verfügbar (während der offenen Beta kann sie jedes Beta-Konto nutzen).
404not_foundProdukt oder Pfad nicht gefunden.
409gtin_takenDie GTIN ist bereits vergeben.
422gtin_not_yoursDie GTIN liegt außerhalb des GS1-Firmenpräfixes Ihres Kontos (Einstellungen → GS1-Firmenpräfix). Tritt nur auf, wenn Sie ein Firmenpräfix gesetzt haben.
403plan_limitSie haben das Kontingent dieser Ebene erreicht (Tarif + Extra). Bei Massenaufrufen landen die überzähligen Einträge in errors.
422validationUngültige Daten (Details in errors).
403read_onlySie haben mit einem Nur-Lese-Schlüssel zu schreiben versucht (POST/PATCH/DELETE).
429rate_limitedSie haben das Anfragekontingent pro Minute überschritten. Der Retry-After-Header gibt an, wie lange zu warten ist.
400bad_jsonDer Anfragekörper ist kein gültiges JSON. Die API nimmt ausschließlich einen JSON-Körper an (Content-Type: application/json).
413too_bigDas hochgeladene Dokument überschreitet die erlaubte Größe. Das tatsächliche Limit nennt GET /health in limits.document_upload_max_bytes.
413payload_too_largeDer Anfragekörper ist nie angekommen — er lief in das post_max_size-Limit des Servers. Nicht das JSON ist schuld, sondern die Größe.
422bad_cursorDer cursor ist kein von uns ausgegebener next_cursor. Geben Sie ihn unverändert zurück — der Cursor ist opak.
422bad_tierAuf dieser Zugriffsstufe kann kein Link ausgegeben werden (nur legitimate_interest und authority).
501needs_migrationDie Funktion erfordert eine noch nicht aktivierte Datenbankaktualisierung. Wir melden bewusst einen Fehler, statt halbe Daten zu liefern — melden Sie sich bei uns, dann schalten wir sie frei.

BatteryPass-Wirtschaftsakteur-API

Das obige /api/v1 ist unsere Schnittstelle. Daneben betreiben wir eine normkonforme: die Endpunkte der CEN-CENELEC JTC 24 Lebenszyklus-API (EN 18222), „Digital Product Passport – Life Cycle API v1.1" — in der Sprache, die der Rest des Batterie-Ökosystems spricht. Nutzen Sie sie, wenn Ihr Partner eine standardisierte Anbindung erwartet und nicht unsere API.

Konformität: die Schnittstelle besteht alle 11 Tests der BatteryPass-Ready-Konformitätssuite (Lesen über DPP-ID und über Produkt-ID, Lesen eines Datenelements, Filterung der Zugriffsrechte, zurückgezogener Pass, Änderung, Hinzufügen und Löschen eines Attributs, Berechtigungsprüfung, Inverkehrbringen). Geprüft wird ein laufendes System, keine hochgeladene Datei.

Basis-URL und Authentifizierung

https://veridyn.eu/eo — Authentifizierung: Authorization: Bearer {JWT}. Das Token trägt eine Rolle (public, purchaser, authority, economic_operator…), und die Antwort wird feldweise danach gefiltert — mit derselben Mechanik, die auch den öffentlichen Pass filtert. Schreiben darf nur der Wirtschaftsakteur.

Woher das Token kommt

Das Token fordern Sie mit Ihrem eigenen Entwicklerschlüssel an — eine separate Registrierung gibt es nicht:

curl -X POST https://veridyn.eu/api/v1/eo/token \
  -H "Authorization: Bearer vk_<kulcs>" \
  -H "Content-Type: application/json" \
  -d '{"ttl":3600}'
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs…",
    "token_type": "Bearer",
    "role": "economic_operator",
    "expires_in": 3600,
    "expires_at": "2026-09-25T11:14:02+00:00",
    "api_base": "https://veridyn.eu/eo"
  }
}

Das Token gilt für das Konto des Schlüssels — den Mandanten entnehmen wir bewusst nicht der Anfrage, ein Token für die Pässe eines anderen Kontos ist damit nicht anforderbar. Als Rolle ist nur economic_operator möglich: die Rolle „das ist mein Produkt". authority oder recycler sprechen im Namen einer anderen Partei — das kann sich niemand selbst zuteilen, dafür gibt es die ablaufenden Zugriffslinks.

Der ttl-Wert in Sekunden, zwischen 5 Minuten und 24 Stunden (Standard 1 Stunde). Die Obergrenze ist bewusst gesetzt: Ein JWT ist selbsttragend, wir führen keine Sperrliste, ein verlorenes langlebiges Token könnten wir also nicht widerrufen. Kurzes Token plus erneute Anforderung ist das richtige Muster. Aus einem Nur-Lese-Schlüssel entsteht kein Token (403 read_only), denn die Rolle des Wirtschaftsakteurs berechtigt auch zum Schreiben.

MethodePfadBeschreibung
POST/dppsNeuer Pass aus dem übermittelten JTC24-Dokument. Antwort: die Kennung (Zeichenkette), 201.
GET/dpps/{dppId}Lesen über die Pass-Kennung.
PATCH/dpps/{dppId}Teilweise Änderung (RFC 7396) — erzeugt eine neue Version. Der Wert null löscht das Feld.
DELETE/dpps/{dppId}Rücknahme. Der Pass bleibt erhalten (Behörden können ihn rückwirkend anfordern), liefert über diese Schnittstelle jedoch 404.
GET/PATCH/dpps/{dppId}/elements/{path}Lesen/Ändern eines einzelnen Datenelements über einen Punktpfad.
GET/PATCH/dpps/{dppId}/collections/{group}Eine Datengruppe (z. B. performanceAndDurability).
GET/dppsByProductId/{productId}Die aktive Version anhand der Produktkennung.
GET/dppsByProductIdAndDate/{productId}Die zum angegebenen Zeitpunkt gültige Version. Parameter: date.
POST/dppsByProductIdsMassenauflösung von Kennungen. Parameter: limit, cursor.

Sicherung und Register: Nach (EU) 2023/1542 müssen die Passdaten auch bei einem unabhängigen Anbieter gespeichert und das Inverkehrbringen gemeldet werden. Ist für das Konto ein Sicherungsanbieter hinterlegt, geht nach jedem Anlegen und Ändern automatisch eine Kopie an ihn — und jeder Aufruf bleibt nachvollziehbar.

Webhooks

Unter Einstellungen → Webhooks registrieren Sie eine HTTPS-URL. Sobald ein Produktpass erstellt / aktualisiert / archiviert wird, sendet Veridyn ein signiertes JSON per POST an diese URL — so wird Ihr System in Echtzeit benachrichtigt, ohne Polling.

Ereignisse

product.created · product.updated · product.archived · product.restored · children.updated · scan.milestone · scan.clone_suspected

Das Ereignis children.updated betrifft die Massenaktualisierung von Kind-Pässen und ist bewusst ein einziges, zusammenfassendes Ereignis — nicht eines pro Zeile. Daten: { parent_id, category, count, items }, wobei items die tatsächlich geänderten Kind-Pässe als { id, serial, lot } auflistet (unchanged-Zeilen bleiben außen vor). Die zeilenweise Begründung steht in der Antwort des Aufrufs, nicht im Webhook — sie geht an den Aufrufer, der die Aktualisierung ausgelöst hat.

scan.milestone wird ausgelöst, wenn die Scan-Zahl eines Produktpasses einen Meilenstein überschreitet (10, 50, 100, 250, 500, 1000, …) — z. B. „Ihr Produktpass wurde 1000-mal geöffnet“. Daten: { product_id, count, milestone }.

scan.clone_suspected weist auf eine mögliche Fälschung hin: Ein eindeutiger (serialisierter) Pass wurde ungewöhnlich oft und aus vielen verschiedenen Ländern bzw. von vielen Geräten gescannt — das kann auf einen kopierten QR-Code hindeuten. Daten: { product_id, count, countries, unique_devices }.

Zustellung

POST https://your-system.example/veridyn-webhook
Content-Type: application/json
X-Veridyn-Event: product.updated
X-Veridyn-Event-Id: evt_9f21c4a08b73d5e6f1a20c44
X-Veridyn-Timestamp: 2026-07-02T13:19:11+00:00
X-Veridyn-Signature: sha256=<hmac>

{
  "event_id": "evt_9f21c4a08b73d5e6f1a20c44",
  "event": "product.updated",
  "data": { "id": "a1993ef7-…", "category": "textile", "version_no": 2 },
  "sent_at": "2026-07-02T13:19:11+00:00"
}
💡 event_id und sent_at stehen auch im Rumpf, nicht nur in Headern — die Signatur deckt sie also ab und sie sind nicht fälschbar. In den Headern stehen sie zusätzlich, damit Sie bereits vor dem Parsen des Rumpfes protokollieren und Duplikate filtern können; weichen beide voneinander ab, gilt der signierte Wert im Rumpf.

Signaturprüfung

X-Veridyn-Signature ist die HMAC-SHA256-Signatur des Roh-Bodys mit dem Secret des Webhooks (in den Einstellungen sichtbar). So prüfen Sie sie — z. B. in PHP:

$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_VERIDYN_SIGNATURE'] ?? '')) {
    http_response_code(401); exit; // ungültige Signatur
}

Antworten Sie mit einem 2xx-Status. Schlägt die Zustellung fehl (kein 2xx / Zeitüberschreitung), wiederholen wir sie automatisch mit exponentiellem Backoff (etwa 1 Min. → 5 Min. → 30 Min. → 2 Std. → 6 Std., max. 6 Versuche) und geben bei dauerhaftem Fehler auf (nach etwa 8,5 Stunden). Der Status der letzten Zustellung ist in den Einstellungen sichtbar und kann dort auch manuell erneut gesendet werden.

Duplikat-Filterung und Nachholen verpasster Ereignisse

Wiederholte Zustellversuche bedeuten, dass dasselbe Ereignis mehrfach eintreffen kann — etwa wenn Ihre Antwort in eine Zeitüberschreitung läuft, die Verarbeitung bei Ihnen aber bereits abgeschlossen war. Deshalb trägt jedes Ereignis eine event_id: Speichern Sie die bereits verarbeiteten und verwerfen Sie jede schon gesehene Kennung. Besonders wichtig ist das bei einem children.updated-Massenereignis, wo ein einziges Duplikat die doppelte Verarbeitung von 500 Zeilen bedeuten würde.

War Ihr System längere Zeit nicht erreichbar (Wartung, Wochenend-Ausfall), können die 6 Versuche aufgebraucht sein und das Ereignis wäre verloren. Dafür gibt es GET /events: Der Endpunkt liefert alle ausgehenden Ereignisse in zeitlicher Reihenfolge samt Zustellstatus. Merken Sie sich die zuletzt verarbeitete id und setzen Sie dort fort — nicht über Seitenzahlen, da zwischenzeitlich neue Ereignisse entstehen können.

curl "https://veridyn.eu/api/v1/events?after_id=1487&limit=100" \
  -H "Authorization: Bearer vk_<kulcs>"
{
  "data": [
    {
      "id": 1488,
      "event_id": "evt_9f21c4a08b73d5e6f1a20c44",
      "event": "product.updated",
      "data": { "id": "a1993ef7-…", "category": "textile", "version_no": 2 },
      "sent_at": "2026-07-02T13:19:11+00:00",
      "delivery": { "status": "failed", "attempts": 6, "http_status": 502, "url": "https://…" }
    }
  ],
  "after_id": 1487,
  "next_id": 1488
}

Der delivery.status ist delivered (erfolgreich zugestellt), pending (wird noch versucht) oder failed (aufgegeben). Das Ereignis erscheint auch dann, wenn die Zustellung nie gelungen ist — genau das macht das Nachholen möglich.

💡 Die Kategorieschemata (welche Felder Pflicht und welche optional sind) sehen Sie im Produktformular und über den Endpunkt /schema/{category}. Derzeit verfügbar: textile, battery und furniture. Das Schema wird erweitert, sobald die EU weitere Kategorien festlegt.

← Einstellungen / API-Schlüssel