Zum Inhalt springen

Konventionen

Konventionen

Die Regeln, die auf jedem Endpunkt gelten — hier einmal formuliert, statt auf allen fünfundzwanzig wiederholt.

Idempotenz

Endpunkte, die etwas verändern, akzeptieren einen Idempotency-Key-Header. Eine Anfrage mit demselben Schlüssel spielt die ursprüngliche Antwort erneut ab, statt die Wirkung zu wiederholen — eine Wiederholung nach einer Zeitüberschreitung kann also keinen zweiten Produktpass anlegen. Schlüssel gehören zu einer Organisation, sodass zwei Kunden nie auf demselben Wert kollidieren. Test und Live bleiben getrennt: ein im einen Modus bereits verwendeter Schlüssel wird im anderen abgelehnt statt abgespielt. Eine Ausnahme, und zwar eine Berechtigung statt einer Tatsache: Die von `POST /documents` zurückgegebene signierte Upload-URL wird bei einer Wiederholung neu ausgestellt, solange die Datei noch aussteht, und entfällt, sobald sie eingegangen ist — eine abgelaufene URL wäre die getreue Wiedergabe von nichts Brauchbarem.

Ein Schlüssel bleibt 30 Tage lang gespeichert. Danach ist er vollständig vergessen, und eine Wiederholung damit läuft erneut durch, statt abgespielt zu werden — ein Abgleichlauf, der später einsetzen kann, sollte daher einen frischen Schlüssel erzeugen.

Anfrage
curl "https://passportcraft.com/api/v1/organizations/{organization}/passports" \
  -X POST \
  -H "Authorization: Bearer $PASSPORTCRAFT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"category":"textile"}'

Blätterung

Sammlungen werden per Cursor geblättert, niemals per Offset. Ein Offset ist bei gleichzeitigen Schreibvorgängen schlicht falsch: ein zwischen zwei Seitenabrufen eingefügter Datensatz verschiebt jeden späteren, und ein Client überspringt oder verdoppelt Zeilen, ohne es zu merken.

Senden Sie next_cursor als cursor zurück, um die nächste Seite zu holen. Ein Cursor kodiert die Filter, unter denen er ausgegeben wurde; mit anderen Filtern wird er abgelehnt, statt eine Seite zu liefern, die stillschweigend etwas anderes bedeutet.

Antwort
{
  "object": "list",
  "data": [],
  "has_more": true,
  "next_cursor": "eyJjIjoiMjAyNi0wOC0wMVQxNDowMzoyMi41MDFaIn0"
}

Bedingte Schreibvorgänge

Beim Lesen eines Produktpasses wird seine Version zurückgegeben, im Antwortkörper und als ETag. Nehmen Sie die Version aus dem Antwortkörper, statt das ETag zu zerlegen — unser CDN kann das ETag beim Komprimieren abschwächen, sodass beim Entfernen der Anführungszeichen ein W/ stehen bleiben kann. Senden Sie diese Version bei einem PATCH als Expected-Version zurück: Hat sich der Produktpass zwischenzeitlich geändert, wird der Schreibvorgang abgelehnt — der einzige Weg, ein Lesen-Ändern-Schreiben gegen einen gleichzeitig arbeitenden Bearbeiter abzusichern. Verwenden Sie If-Match nicht. Die beiden Endpunkte, die ihn lesen — einen Produktpass aktualisieren und ein Dokument lösen —, lehnen alles außer dem Platzhalter * ab, denn unser CDN vergleicht den Header mit der Antwort und schreibt das Ergebnis um: Der Schreibvorgang würde ausgeführt und Ihnen trotzdem als Ablehnung gemeldet. Sonst ignorieren wir ihn, unser CDN aber vielleicht nicht: Wir haben gesehen, wie es einen Erfolg nach unserer Antwort durch einen 412 ersetzt. Senden Sie den Header daher am besten gar nicht. Ein erfolgreicher PATCH gibt selbst kein ETag zurück; die neue Version steht im Antwortkörper, im Feld version. Wiederholen Sie einen abgelehnten Schreibvorgang mit einem NEUEN Idempotency-Key: Die Ablehnung ist unter dem alten gespeichert und würde erneut ausgeliefert.

Anfrage
curl "https://passportcraft.com/api/v1/organizations/{organization}/passports/{id}" \
  -X PATCH \
  -H "Authorization: Bearer $PASSPORTCRAFT_API_KEY" \
  -H "Expected-Version: 4" \
  -H "Content-Type: application/json" \
  -d '{"data":{"recycled_content_percentage":38}}'

Ratenbegrenzungen

Bei jeder Anfrage mit Zugangsschlüssel laufen drei Zähler gleichzeitig, und der engste entscheidet. Die veröffentlichten Werte sind bewusst vorsichtige Ausgangspunkte, keine austarierten Obergrenzen.

ZählerAnfragenZeitfenster
key_org100060s
organization300060s
credential1000060s
destructive503600s

Jede gezählte authentifizierte Antwort enthält RateLimit- und RateLimit-Policy-Header, die den Zähler nennen, der seinem Limit am nächsten kam. Eine Ablehnung ergänzt Retry-After, auf jedem Pfad. Warten Sie diese Zeit ab, statt zu raten.

Eine Anfrage ohne brauchbaren Zugangsschlüssel zählt gegen einen eigenen, deutlich engeren Zähler von 60 Anfragen pro Minute je Clientadresse. Für einen gültigen Schlüssel gilt er nie.

Anfragegröße

Ein Anfragekörper über der Obergrenze seines Endpunkts wird mit payload_too_large abgelehnt, bevor irgendetwas Richtung Datenbank gelesen wird. Die Obergrenze wird zweimal geprüft: zuerst gegen die angegebene Content-Length, was nichts kostet, und dann gegen die tatsächlich eintreffenden Bytes — so bleibt auch eine Anfrage ohne Längenangabe begrenzt.

EndpunkteObergrenze
Jeder Endpunkt, der einen einzelnen Datensatz schreibt256 KiB (262144 bytes)
Massenimport und Einheiten-Stapel4 MiB (4194304 bytes)

Die Obergrenze für Stapel folgt dem größten Stapel, den diese API annimmt, nicht einer runden Zahl: 500 Importzeilen mit jedem Feld einer Kategorie befüllt oder 1000 serialisierte Einheiten. Keiner von beiden passt in die Standardobergrenze — genau deshalb haben diese beiden Endpunkte eine eigene.

Ein Anfragekörper muss als application/json eintreffen; application/merge-patch+json wird ebenfalls angenommen. Alles andere wird mit unsupported_media_type abgelehnt, statt im Parser zu scheitern — der Fall, den man kennen sollte, ist curl -d: es sendet application/x-www-form-urlencoded, sofern nichts anderes angegeben wird. Ein Körper ganz ohne Content-Type wird als JSON gelesen.

HTTP-Methoden

Ein Endpunkt beantwortet nur die Methoden, die er dokumentiert. Jede andere Methode wird mit method_not_allowed abgelehnt, zusammen mit einem Allow-Header, der die funktionierenden nennt — wer zum falschen Verb greift, erfährt das richtige also aus der Ablehnung selbst. HEAD funktioniert überall dort, wo GET funktioniert, und OPTIONS überall.

Antwort
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, PATCH, OPTIONS
Content-Type: application/json

{
  "error": {
    "code": "method_not_allowed",
    "documentation_url": "https://passportcraft.com/docs/api/errors#method_not_allowed",
    "request_id": "req_9Fv2KpQ0aXbT4Lmn",
    "details": { "method": "DELETE", "allowed": ["GET", "HEAD", "PATCH", "OPTIONS"] }
  }
}

Destruktive Operationen

Einen Produktpass zurückzuziehen und einen in den Papierkorb zu verschieben nimmt jeweils einen veröffentlichten Datensatz vom Markt. Beide zahlen daher auf eine eigene Obergrenze neben der gewöhnlichen Ratenbegrenzung ein: eine Schleife billiger, wohlgeformter Anfragen darf keinen ganzen Katalog dunkel schalten können.

Ein Dokument an ein Feld anhängen

Manche URL-Felder nehmen statt eines Links auch eine hochgeladene Datei entgegen – etwa einen Prüfbericht oder eine Konformitätserklärung. Laden Sie die Datei zuerst hoch und verweisen Sie das Feld anschließend darauf, indem Sie die Dokument-ID in den Begleitschlüssel dieses Feldes in `data` schreiben. Das Feld selbst darf leer bleiben: Ein dokumentfähiges Feld gilt als ausgefüllt, sobald ein Dokument angehängt ist – ein Produktpass mit leerer URL kann also trotzdem validiert und veröffentlicht werden.

Sie müssen diesen Schlüssel nie selbst zusammensetzen. Ein Feld, das Dokumente annimmt, führt im Kategorieschema zwei Attribute: `document_types` mit den zulässigen Dokumentarten und `document_id_key` mit dem exakt zu schreibenden Schlüssel. Lesen Sie den Schlüssel aus dem Schema, statt sich auf die Namensregel zu verlassen – das ist der einzige unterstützte Weg. Der Begleitschlüssel ist eine Eigenschaft des Feldes und kein eigenes Feld; in der Liste `fields` des Schemas taucht er deshalb nicht auf.

Eine Regel, die das Schema nicht ausbuchstabiert: Der `access_tier` des Dokuments muss dem des Feldes entsprechen. Ein öffentliches Dokument kann ein Feld der Stufe „authority“ nicht erfüllen; das Anhängen wird mit `access_tier_mismatch` abgelehnt, denn ein Dokument mit abweichender Stufe würde dort, wo das Feld erscheint, gar nicht angezeigt. Legen Sie das Dokument mit dem `access_tier` an, den der Felddeskriptor nennt.

Anfrage
curl "https://passportcraft.com/api/v1/organizations/{organization}/passports/{id}" \
  -X PATCH \
  -H "Authorization: Bearer $PASSPORTCRAFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"data":{"conformity_declaration_url__document_id":"doc_123"}}'

Leeren Sie den Begleitschlüssel, um das Dokument zu lösen; die Datei bleibt in Ihrer Bibliothek, bis Sie sie löschen. Wird ein Dokument gelöscht, auf das noch ein Feld verweist, meldet die Antwort, was dabei gelöst wurde – und diese Meldung erreicht nur den Aufrufer, der die Anfrage gestellt hat.

Der `upload_state` eines Dokuments ist `ready`, `pending` oder `unknown`. `unknown` bedeutet, dass wir nicht feststellen konnten, ob die Bytes im Speicher liegen — entweder hat der Speicher nicht geantwortet, oder der Datensatz lag jenseits der 25 Dokumente ohne Bytes, die eine Anfrage prüft. Werten Sie das als unbestimmt, nicht als fehlend: Beim Veröffentlichen führt `unknown` zu `storage_unavailable` statt zu der Aussage, eine Datei fehle, und ein späterer Abruf klärt es in der Regel.

Felder, die nur für bestimmte Produkte gelten

Das Kennzeichen `required` eines Feldes besagt, dass die Veröffentlichungsprüfung dieses Feld verlangen kann. Hängt das Verlangen vom Produkt selbst ab, trägt das Feld zusätzlich `required_when` und nennt darin ein weiteres Feld sowie die Werte, die das Verlangen auslösen. Der Referenztest für die Zyklenlebensdauer trägt `required_when` für `battery_category` mit `ev` und `lmt`: Die Batterieverordnung nimmt die Passdaten „in dem für die Kategorie oder Unterkategorie der betreffenden Batterie geltenden Umfang“ auf, und der Leitfaden der Kommission zum Batteriepass ordnet jeden Datenpunkt den Kategorien zu, für die er gilt. Eine Kategorie kann ein Feld auch über eine Regel zurückfordern, die mehrere Felder zugleich liest — `required_when` beschreibt diese nicht, und aufgelöst werden sie am Validate-Endpunkt.

Lesen Sie beide zusammen und behandeln Sie `required` als die weitere Antwort: Ein Client, der `required_when` ignoriert, fragt höchstens mehr ab als nötig. Er hält einen Produktpass nie fälschlich für fertig und erhält keine abgelehnte Veröffentlichung. Für einen bestimmten Produktpass gibt der Validate-Endpunkt die genaue Antwort, denn ihm liegt dieser Pass vor.

Felder, die ab einem Datum erforderlich sind

Ein Feld mit `required_from` wird ab diesem Datum nach Brüsseler Zeit für die Veröffentlichung erforderlich; davor ist es optional. Wir veröffentlichen ein solches Datum so früh wie möglich, in der Regel mindestens 60 Tage im Voraus; legt eine Verordnung ein Datum mit kürzerem Vorlauf fest oder ändert es, gilt für das Feld das Datum der Verordnung. Lesen Sie `required` zusammen mit `required_when` und `required_from`; der Validate-Endpunkt gibt die Antwort für einen einzelnen Produktpass.

Batteriemesswerte

Jeder Messwert hält einen Wert, seine Quelle und das Datum der Messung oder Angabe fest. Erfassen Sie zur Korrektur einen weiteren Messwert. Das jüngste Beobachtungsdatum zählt; bei gleichem Datum zählt der später erfasste Eintrag.

per_battery: override

Eine Batterie verwendet den Wert ihres Modells, bis für dieses Feld ein eigener Messwert vorliegt. Ein Modellwert hat kein Messdatum.

Ausgangswerte für einzelne Batterien (optional)

model_health_declaration
  • Solange die Erklärung gilt, stammt jeder Wert zum Alterungszustand vom Modell, bis Sie diesen Wert für die Batterie erfassen.
  • Fehlende Modellwerte bleiben leer. Mit dieser Erklärung wird der zertifizierte Zustand einer Batterie (SOCE) anfangs mit 100 % angegeben.
  • Ein erfasster Wert ersetzt die Modellerklärung nur für das betreffende Feld.
  • Wenn Sie den Status zum ersten Mal speichern, werden die Werte zum Alterungszustand aus der Erklärung weiter verwendet. Wenn Sie erneut einen Status speichern, liefert die Erklärung keine Werte zum Alterungszustand mehr für diese Batterie. Dies gilt auch, wenn Sie denselben Status wählen.
  • Bereits für diese Batterie erfasste Werte bleiben erhalten.

Fehlende erforderliche Batteriewerte werden in missing_fields gemeldet. Sie verhindern weder das Anlegen einer Batterie noch die Veröffentlichung ihres Modells.

Anfragekennungen

Jede authentifizierte Antwort enthält einen Request-Id-Header, im Erfolgsfall ebenso wie im Fehlerfall. Geben Sie ihn in einer Support-Anfrage an, dann lässt sich der genaue Aufruf finden. Die beiden Kategorie-Endpunkte beantworten erfolgreiche Anfragen ohne Schlüssel aus einem gemeinsamen Cache; diese Antworten enthalten keine Request-Id — eine Ablehnung enthält sie immer, auf jedem Pfad.

Der Ereignis-Feed hält 30 Tage Historie vor. Eine Integration, die seltener abfragt, verpasst Änderungen und sollte stattdessen gegen die Produktpass-Liste abgleichen.