Naar de inhoud

Afspraken

Afspraken

De regels die op elk endpoint gelden, hier één keer opgeschreven in plaats van vijfentwintig keer herhaald.

Idempotentie

Endpoints die iets wijzigen accepteren een Idempotency-Key-header. Een verzoek met dezelfde sleutel herhalen speelt het oorspronkelijke antwoord opnieuw af in plaats van het effect te herhalen, dus een nieuwe poging na een time-out kan geen tweede paspoort aanmaken. Sleutels horen bij één organisatie, zodat twee klanten nooit op dezelfde waarde botsen. Test en productie zijn gescheiden: een sleutel die in de ene modus al gebruikt is, wordt in de andere geweigerd in plaats van afgespeeld. Eén uitzondering, en die is een bevoegdheid en geen feit: de ondertekende upload-URL van `POST /documents` wordt bij een herhaling opnieuw uitgegeven zolang het bestand nog ontbreekt, en weggelaten zodra het binnen is — een verlopen URL zou een getrouwe herhaling van niets bruikbaars zijn.

Een sleutel wordt 30 dagen onthouden. Daarna is hij volledig vergeten en wordt een nieuwe poging ermee opnieuw uitgevoerd in plaats van afgespeeld — een afstemmingstaak die later kan draaien, kan dus beter een verse sleutel aanmaken.

Verzoek
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"}'

Paginering

Verzamelingen worden met een cursor gepagineerd, nooit met een offset. Een offset is onjuist zodra er gelijktijdig geschreven wordt: een record dat tussen twee pagina-opvragingen wordt ingevoegd verschuift alle latere records, en een client slaat rijen over of dupliceert ze zonder het te merken.

Stuur next_cursor terug als cursor om de volgende pagina op te halen. Een cursor codeert de filters waaronder hij is uitgegeven, dus met andere filters wordt hij geweigerd in plaats van beantwoord met een pagina die stilzwijgend iets anders betekent.

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

Voorwaardelijke schrijfacties

Het lezen van een paspoort geeft de versie terug, in de antwoordinhoud en als ETag. Neem de versie uit de antwoordinhoud in plaats van de ETag te ontleden: ons CDN kan de ETag bij compressie verzwakken, dus na het weghalen van de aanhalingstekens kan een W/-voorvoegsel achterblijven. Stuur die versie bij een PATCH terug als Expected-Version: is het paspoort intussen gewijzigd, dan wordt de schrijfactie geweigerd — de enige manier om lezen, wijzigen en schrijven veilig te maken tegenover een gelijktijdige bewerker. Gebruik If-Match niet. De twee eindpunten die hem lezen — een paspoort bijwerken en een document losmaken — weigeren elke waarde behalve het jokerteken *, want ons CDN vergelijkt de header met het antwoord en herschrijft de uitkomst, zodat de schrijfactie zou worden uitgevoerd terwijl u toch een weigering te zien krijgt. Elders negeren wij hem, ons CDN misschien niet: wij hebben het een succes na ons antwoord door een 412 zien vervangen, dus stuur de header het best helemaal niet. Een geslaagde PATCH geeft zelf geen ETag terug; de nieuwe versie staat in de antwoordinhoud, in het veld version. Probeer een geweigerde schrijfactie opnieuw met een NIEUWE Idempotency-Key: de weigering is onder de oude vastgelegd en zou opnieuw worden teruggegeven.

Verzoek
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}}'

Snelheidslimieten

Bij elk verzoek met een geldige sleutel lopen drie tellers tegelijk en de smalste beslist. De gepubliceerde cijfers zijn bewust behoedzame beginwaarden, geen fijngeregelde plafonds.

TellerVerzoekenVenster
key_org100060s
organization300060s
credential1000060s
destructive503600s

Elk geteld geauthenticeerd antwoord bevat RateLimit- en RateLimit-Policy-headers die de teller noemen die zijn limiet het dichtst naderde. Een weigering voegt Retry-After toe, op elk pad. Wacht die tijd af in plaats van te gokken.

Een verzoek dat binnenkomt zonder bruikbare sleutel wordt gemeten met een aparte, veel strakkere teller van 60 verzoeken per minuut per clientadres. Voor een geldige sleutel geldt hij nooit.

Omvang van het verzoek

Een verzoeklichaam boven het plafond van zijn endpoint wordt geweigerd met payload_too_large, voordat er iets richting de database wordt gelezen. Het plafond wordt twee keer getoetst: eerst aan de opgegeven Content-Length, wat niets kost, en daarna aan de bytes die werkelijk binnenkomen — zo blijft ook een verzoek zonder opgegeven lengte begrensd.

EndpointsPlafond
Elk endpoint dat één record schrijft256 KiB (262144 bytes)
Bulkimport en eenhedenbatches4 MiB (4194304 bytes)

Het bulkplafond volgt de grootste batch die deze API aanneemt, niet een rond getal: 500 importregels met elk veld van een categorie ingevuld, of 1000 geserialiseerde eenheden. Geen van beide past binnen het standaardplafond, en precies daarom hebben die twee endpoints een eigen plafond.

Een lichaam moet binnenkomen als application/json; application/merge-patch+json wordt ook aanvaard. Al het andere wordt geweigerd met unsupported_media_type in plaats van te stranden in de parser — het geval om te kennen is curl -d, dat application/x-www-form-urlencoded stuurt tenzij anders opgegeven. Een lichaam zonder enige Content-Type wordt als JSON gelezen.

HTTP-methoden

Een endpoint beantwoordt alleen de methoden die het documenteert. Elke andere methode wordt geweigerd met method_not_allowed en een Allow-header die de werkende methoden noemt, zodat een client die naar het verkeerde werkwoord grijpt het juiste uit de weigering zelf leert. HEAD werkt overal waar GET werkt, en OPTIONS werkt overal.

Antwoord
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"] }
  }
}

Ingrijpende handelingen

Een paspoort terugtrekken en er een naar de prullenbak verplaatsen halen allebei een gepubliceerd record uit de markt, dus tellen ze mee voor een apart plafond naast de gewone snelheidslimiet. Een lus van goedkope, correct opgebouwde verzoeken mag geen hele catalogus kunnen uitschakelen.

Een document aan een veld koppelen

Sommige url-velden accepteren een geüpload bestand in plaats van een link — bijvoorbeeld een testrapport of een conformiteitsverklaring. Upload eerst het bestand en laat het veld er vervolgens naar verwijzen door de document-id naar de bijbehorende sleutel van dat veld in `data` te schrijven. Het veld zelf mag leeg blijven: een veld dat documenten accepteert geldt als ingevuld zodra er een document aan hangt, dus een paspoort met een lege url kan nog steeds worden gevalideerd en gepubliceerd.

U hoeft die sleutel nooit zelf samen te stellen. Een veld dat documenten accepteert draagt twee attributen in het categorieschema: `document_types`, met de toegestane soorten, en `document_id_key`, met de precieze sleutel die u moet schrijven. Lees de sleutel uit het schema in plaats van uit te gaan van de naamgevingsregel; dat is de enige ondersteunde manier. De bijbehorende sleutel is een eigenschap van het veld en geen zelfstandig veld, en verschijnt daarom nooit in de lijst `fields` van het schema.

Eén regel die het schema niet voor u uitschrijft: de `access_tier` van het document moet gelijk zijn aan die van het veld. Een openbaar document kan geen veld van niveau ‘authority’ vervullen; de koppeling wordt geweigerd met `access_tier_mismatch`, omdat een document met een afwijkend niveau niet zou worden getoond waar het veld wel wordt getoond. Maak het document aan met de `access_tier` die de velddescriptor vermeldt.

Verzoek
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"}}'

Maak de bijbehorende sleutel leeg om het document los te koppelen; het bestand blijft in uw bibliotheek totdat u het verwijdert. Verwijdert u een document waar nog een veld naar verwijst, dan meldt het antwoord wat er is losgekoppeld, en die melding bereikt alleen de aanroeper die het verzoek deed.

De `upload_state` van een document is `ready`, `pending` of `unknown`. `unknown` betekent dat we niet konden vaststellen of de bytes in de opslag staan: ofwel gaf de opslag geen antwoord, ofwel viel de rij voorbij de 25 documenten zonder bytes die één verzoek controleert. Beschouw het als onbepaald, niet als afwezig: bij publiceren leidt `unknown` tot `storage_unavailable` in plaats van de melding dat een bestand ontbreekt, en een latere leesactie geeft doorgaans uitsluitsel.

Velden die maar voor sommige producten gelden

De aanduiding `required` van een veld betekent dat de publicatiecontrole dit veld kan eisen. Hangt die eis van het product zelf af, dan draagt het veld ook `required_when`, met daarin een ander veld en de waarden die de eis inschakelen. De referentietest voor de cyclusduur draagt `required_when` voor `battery_category` met `ev` en `lmt`: de batterijenverordening neemt de paspoortgegevens op “voor zover die van toepassing is op de categorie of subcategorie van de betrokken batterij”, en de richtsnoeren van de Commissie over het batterijpaspoort koppelen elk gegevenspunt aan de categorieën waarvoor het geldt. Een categorie kan een veld ook terugvragen via een regel die meerdere velden tegelijk leest — `required_when` beschrijft die niet, en het validatie-eindpunt lost ze op.

Lees beide samen en behandel `required` als het ruimere antwoord: een client die `required_when` negeert, vraagt hooguit meer dan nodig. Hij houdt een paspoort nooit ten onrechte voor gereed om de publicatie daarna geweigerd te zien. Voor één bepaald paspoort geeft het validatie-eindpunt het precieze antwoord, want dat heeft dat paspoort voor zich.

Velden die vanaf een datum verplicht zijn

Een veld met `required_from` wordt op die datum, volgens de tijd in Brussel, verplicht voor publicatie; daarvoor is het optioneel. Wij publiceren zo’n datum zo vroeg mogelijk, doorgaans minstens 60 dagen van tevoren; als een verordening op kortere termijn een datum vastlegt of wijzigt, volgt het veld de datum uit de verordening. Lees `required` samen met `required_when` en `required_from`; het validatie-eindpunt geeft het antwoord voor één specifiek paspoort.

Batterijmetingen

Elke meting legt een waarde, de bron en de datum van meting of verklaring vast. Corrigeer een waarde door een nieuwe meting vast te leggen. De meest recente waarnemingsdatum geldt; bij gelijke datums geldt de later vastgelegde invoer.

per_battery: override

Een batterij gebruikt de waarde van haar model totdat zij voor dat veld een eigen meting heeft. Een modelwaarde heeft geen meetdatum.

Beginwaarden voor afzonderlijke batterijen (optioneel)

model_health_declaration
  • Zolang de verklaring geldt, komt elke gezondheidswaarde van het model totdat u die waarde voor de batterij vastlegt.
  • Ontbrekende modelwaarden blijven leeg. Met deze verklaring begint de staat van gecertificeerde energie op 100%.
  • Een vastgelegde waarde vervangt de modelverklaring alleen voor dat veld.
  • De eerste status die u opslaat, behoudt de verklaring. Als u opnieuw een status opslaat, levert de verklaring geen gezondheidswaarden meer voor die batterij. Dit geldt ook als u dezelfde status kiest.
  • Waarden die al voor die batterij zijn vastgelegd, blijven behouden.

Ontbrekende verplichte batterijwaarden staan in missing_fields. Ze blokkeren het aanmaken van een batterij of publiceren van haar model niet.

Verzoek-identificaties

Elk geauthenticeerd antwoord bevat een Request-Id-header, zowel bij succes als bij een fout. Vermeld deze in een supportverzoek, dan kan de exacte aanroep worden teruggevonden. De twee categorie-endpoints beantwoorden geslaagde verzoeken zonder sleutel vanuit een gedeelde cache; die antwoorden bevatten geen Request-Id — een weigering bevat er altijd een, op elk pad.

De gebeurtenissenstroom bewaart 30 dagen geschiedenis. Een koppeling die minder vaak opvraagt mist wijzigingen en kan beter afstemmen tegen de paspoortenlijst.