Aan de slag

Changelog

Gedateerd overzicht (nieuwste bovenaan) van elke wijziging aan de publieke storefront API. Lees dit voordat je een integratie bijwerkt.

Bekijk als Markdown

De storefront API is geversioneerd via het URL-prefix (/v1). Binnen v1 is elke wijziging additief: velden, endpoints, optionele parameters en enum-waarden worden alleen toegevoegd. Elke vermelding hieronder noemt de datum, wat er veranderde en welke pagina's van deze referentie zijn bijgewerkt. Dezelfde vermeldingen staan in de changelog van de repository.

2026-10-29

Toegevoegd

  • variants[].image_id (uuid of null) en variants[].image (afbeeldingsobject of null) op GET /v1/api/commerce/products en GET /v1/api/commerce/products/{id}: de eigen afbeelding van een variant, een van de media-items van het product, dus image_id komt overeen met een item in images op het detail-endpoint. Producten die uit Shopify zijn geïmporteerd krijgen hem uit de kolom Variant Image. Zie Variantobject.

2026-10-24

Opmerkingen

  • Documentatiefix, niets veranderd in de API. De eventcatalogus van webhooks documenteert nu de acht abonneerbare sales-events die WebhookDispatcher sinds de sales-module toestaat: quote.offered, quote.accepted, quote.declined, quote.expired ({id, number, status, origin, total_amount, currency, email, valid_until, signed_at}) en invoice.sent, invoice.paid, invoice.overdue, credit_note.sent ({id, number, kind, status, total_amount, paid_amount, open_amount, currency, email, issue_date, due_date}), met wanneer elk wordt verstuurd. Zie Webhooks.
  • De scopetabel noemt elke toekenbare scope en de routes die hem controleren: catalog:read (en het paar catalog:write, configurator:*), contacts:read / contacts:write met customers:* als hun vroegere namen, sales:read / sales:write, en welke scopes toekenbaar zijn maar nog door geen /v1/api-route worden gebruikt. Zie Authenticatie.

2026-10-18

Toegevoegd

  • GET /v1/api/forms/address-lookup (scope forms:read): country=NL, postal_code, house_number en een optionele addition geven 200 {street, city, postal_code, lat, lon} (niet verpakt in data). Fouten: 404 (No address found.), 422 op country, postal_code of house_number, 503 (Address lookup is temporarily unavailable.). 60 requests per minuut per winkel, daarna 429. Zie Adres opzoeken.
  • Veldtype address bij formulieren. config is {country, lookup?, service_area?}; het antwoord is {postal_code, house_number, addition?, street, city, lat?, lon?} en een ongeldige waarde geeft Must be a valid address. op answers.{key}. service_area is {origin: {lat, lon, label?}, radius_km, mode: notice | block, message}. Zie Veldtypes.
  • meta.address bij een opgeslagen inzending: per address-veld met een werkgebied {distance_km, in_area}. Bij mode: "block" geeft een adres dat aantoonbaar buiten het gebied ligt 422 op answers.{key} met de message van het veld. Zie Adresvelden en het werkgebied.
  • options_source bij choice-velden: {object_type, label, description?, sort?, limit?}. GET /v1/api/forms/{key} vult config.options bij elk request uit de publiek zichtbare records van de collectie en een inzending wordt daartegen gecontroleerd. Zie Veldtypes.
  • Lengtegrenzen voor de onderdelen van een address-antwoord, na het verwijderen van de spaties: postal_code 12 tekens, house_number 20, addition 10, street en city elk 255. Een langer onderdeel geeft Must be a valid address. op answers.{key}, ook in check() van @borkol/forms. Zie Veldtypes.
  • @borkol/forms 0.2.0: de optie persistKey bij BorkolForm en useBorkolForm bewaart het concept van een bezoeker tot 30 dagen in localStorage, en formatAddress, normaliseAddress en distanceKm worden geëxporteerd. Zie Het pakket @borkol/forms.

Gewijzigd

  • Een formulier mag nu 150 invoervelden hebben (was 60). heading en paragraph tellen niet mee. Stappen blijven op 20. Zie Definitie: stappen en velden.

Opmerkingen

  • address is een nieuw veldtype: een renderer die een component kiest op kind heeft er een nodig. Behandel een onbekend type als additief, zoals altijd binnen /v1.
  • Bij GET /v1/api/forms/{key} is config.service_area.message van een address-veld een tekst in de gevraagde taal (met de gebruikelijke terugval), zoals elke andere tekst van de definitie. Zie Veldtypes.

2026-10-16

Toegevoegd

  • code: "module_disabled" en module (de module-key) op de body van de module-disabled-error, zowel bij de 403- als de 404-variant. error blijft ongewijzigd, dus bestaande checks blijven werken. Zie Module niet ingeschakeld.

2026-10-13

Toegevoegd

  • Formulieren. Een nieuwe module forms en een nieuw hoofdstuk. GET /v1/api/forms/{key}?locale= (scope forms:read) geeft de gepubliceerde versie van een actief formulier terug, verpakt in data: {key, version, name, steps, settings: {thank_you: {message, redirect_url}}}, met elke tekst in één taal en de standaardtaal van de winkel als terugval. Stappen en velden hebben show_if-regels (equals, not_equals, contains, filled, empty, één niveau all of any); de veldtypes zijn text, textarea, number, email, phone, date, select, link, choice, consent, hidden, file, heading en paragraph. Zie Formulieren en Een formulier laden.
  • POST /v1/api/forms/{key}/submissions (scope forms:write) controleert de antwoorden met dezelfde regels tegen de gepubliceerde versie, laat antwoorden op verborgen velden weg, slaat de inzending op en antwoordt 201 {"data": {"number", "thank_you": {message, redirect_url}}}. Fouten volgen de 422-envelope met keys answers.{key} en files.{key}; een honeypot met een andere waarde dan een lege string of null geeft 201 met number: null en slaat niets op. Zie Inzendingen.
  • Header X-Borkol-Visitor-Ip op de formulierroutes: de limiet per uur per formulier (1 tot 100 per bezoeker, standaard 5) telt per bezoekersadres, ook pogingen die met 422 stranden, en antwoordt 429 {"message": "Too many submissions. Try again later."}. Zonder geldige header wordt het adres van de verbinding gebruikt. Zie Bezoekers-IP en de limiet per uur.
  • Scopes forms:read en forms:write, en forms als module-key in 403 {"error": "Module not enabled: forms"}. Houd forms:write-sleutels op je server. Zie Authenticatie.
  • Webhook-events form.published ({key, version}) en form_submission.created ({id, number, form_key, status, locale, name, email, created_at}, nooit de antwoorden). Zie Webhooks.
  • POST /v1/api/forms/{key}/uploads (scope forms:write, multipart field en file) slaat één bestand voor een file-veld privé op en antwoordt 201 {"data": {"token", "name", "size"}}. Het type wordt bepaald aan de hand van de inhoud (image: JPEG, PNG, WebP, HEIC/HEIF; pdf: PDF), de grootte tegen max_mb van het veld (maximaal 20 MB); fouten zijn 422 op field of file. 30 uploads per formulier per bezoeker per uur, daarna 429 {"message": "Too many uploads. Try again later."}. Tokens werken één keer, horen bij het formulier en het veld, en verlopen na 24 uur. Zie Uploads.
  • files op POST /v1/api/forms/{key}/submissions: veld-key naar een lijst uploadtokens, geclaimd met de inzending; een token dat onbekend is, bij een ander formulier of veld hoort, gebruikt of verlopen is, geeft 422 op files.{key} (This upload is invalid or has expired. Upload the file again.). Zie Inzendingen.
  • Cloudflare Turnstile op formulieren: settings.turnstile_site_key op GET /v1/api/forms/{key} als het formulier Turnstile aan heeft en de winkel beide keys heeft ingesteld, en turnstile_token op de inzending, gecontroleerd na de limiet per uur en vóór de antwoorden. Een mislukte controle geeft 422 op turnstile; tokens werken één keer, dus reset de widget na elke 422. Zie Turnstile en Een formulier laden.
  • Het pakket @borkol/forms: types, de zichtbaarheids- en antwoordregels (visible, check, prefill) en een headless React-renderer (@borkol/forms/react) met upload, turnstileToken, onServerErrors en een formError-component. Zie Het pakket @borkol/forms.

Opmerkingen

  • Een formulier met Turnstile aan wordt niet gecontroleerd zolang de winkel niet beide Turnstile-keys heeft ingesteld: turnstile_site_key ontbreekt dan en inzendingen worden zonder controle geaccepteerd.

2026-10-12

Toegevoegd

  • Filteren op een tags-, record_ref_list- of record_ref-property met filter[{propertyKey}]: een tag of record-id die de opgeslagen lijst bevat (een verwijzing matcht op gelijkheid). Zie Records.
  • settings op elk objecttype van GET /v1/api/objects/types: {publishable, slug?}, waarbij slug {source, prefixes} is en alleen aanwezig als het type zijn records adressen geeft. localized (boolean) en config (object of null) op elke propertydefinitie. Zie Types.
  • De propertytypes richtext, datetime, media, media_list, link, icon, token_variant, color, email, phone, tags, record_ref, record_ref_list en een repeater met één niveau nesting voor objectproperties, naast de bestaande types. Properties kunnen worden gelokaliseerd (niet number, money, boolean, date, datetime, customer, color, media_list, recordverwijzingen, repeater, sections; een media_list draagt in plaats daarvan alt en caption als locale-maps). Zie Objecten.
  • published_at op elk record, en slugs (taaltag naar adres) op de records van een type met adressen, beperkt tot de voor de winkel ingeschakelde talen. Zie Records.
  • GET /v1/api/objects/types/{key}/records/by-slug/{slug}?locale= haalt één gepubliceerd record op via zijn adres in één taal (locale is standaard de standaardtaal van de winkel; 422 op locale als die niet is ingeschakeld, 404 als niets zichtbaars matcht). Zie Records.
  • Webhook-events object_record.published, object_record.updated, object_record.unpublished en object_record.deleted, met de payload {id, type_key, slugs, published_at}. Ze gaan alleen af bij echte overgangen; een ingepland record dat zijn tijd bereikt, verstuurt niets. Zie Webhooks.
  • GET /v1/api/redirects accepteert ook tenant:read en objects:read, en stuurt door naar recordadressen op dezelfde manier als naar producten en categorieën (een gewijzigd recordadres legt alleen een redirect vast als het type voor die taal een niet-leeg adresvoorvoegsel heeft). Zie Redirects en sitemap.
  • include=refs (of include[]=refs) op GET /v1/api/objects/types/{key}/records, GET /v1/api/objects/records/{id} en het by-slug-endpoint klapt record_ref- en record_ref_list-waarden uit tot {id, type_key, values, slugs?, published_at}, één niveau diep. Doelen die niet publiek zichtbaar zijn vallen weg (null voor een verwijzing, uit een lijst verwijderd); onbekende include-tokens worden genegeerd. Zie Records.
  • Query-parameter locale op GET /v1/api/objects/types/{key}/records: gelokaliseerde filter[...], sort en q gebruiken die taal. Een tag van een ingeschakelde taal is nodig om te werken; elke andere waarde valt terug op de standaardtaal van de winkel. Zie Records.
  • Objectrecords in GET /v1/api/sitemap: items {type: "object_record", object_type, locale, path, lastmod, alternates}, één per publiek zichtbaar record, taal en adres, voor types met een niet-leeg adresvoorvoegsel. Zie Redirects en sitemap.
  • GET /v1/api/business (scope tenant:read) geeft het openbare bedrijfsprofiel: handels- en juridische naam, contactgegevens, adres, registratie- en BTW-nummers, social_links ({platform, url, label}, 23 platforms), de standaard locale en content_locales. Bank-, ondertekenaar-, afzender- en voorwaardenvelden worden nooit getoond, en een winkel zonder profiel antwoordt 200 met nulls. Zie Bedrijfsprofiel.

Gewijzigd

  • Mediawaarden zijn objecten. Een media-waarde (een property, of een veld binnen een sectie of repeaterrij) is nu {id, url, alt, width, height, mime_type}, of null als het item ontbreekt of privé is. Het was het media-id als string. Een media_list-waarde is een lijst van dezelfde objecten, elk met een caption; items die ontbreken of privé zijn, vallen uit de lijst. Het media-id zelf ophalen is niet meer nodig. Zie Objecten.
  • Concepten en ingeplande records zijn verborgen. Op een type met settings.publishable: true ontbreken concepten en records met een published_at in de toekomst in de recordlijst en geven ze 404 op GET /v1/api/objects/records/{id} en via slug. filter[status] wordt publiek niet geaccepteerd (422 op filter). Typen zonder publicatiestap blijven ongewijzigd. Zie Records en Publiceren en zichtbaarheid.
  • Gelokaliseerde waarden worden teruggesneden tot de ingeschakelde talen. Een gelokaliseerde property of veld geeft een map van taaltag naar waarde met alleen de voor de winkel ingeschakelde talen. Een opgeslagen lege sections-waarde komt terug als []; secties van een type dat niet meer bestaat, vallen weg. Zie Gelokaliseerde waarden.
  • GET /v1/api/redirects en GET /v1/api/sitemap worden door het platform geleverd. Zelfde paden, zelfde responses. Ze vereisen de catalog-module niet meer (geen 403 Module not enabled: catalog meer), accepteren één van de scopes catalog:read, commerce:read, tenant:read of objects:read, en de sitemap lijst alleen de items van ingeschakelde modules (een winkel zonder catalog-module krijgt een lege lijst). De referentiepagina is verplaatst van Catalogus naar Aan de slag. Zie Redirects en sitemap.
  • Gelokaliseerde filters, sortering en zoeken volgen locale. Filteren of sorteren op een gelokaliseerde property, en q op een gelokaliseerde tekstproperty, lezen nu de waarde in de gevraagde taal (de standaardtaal van de winkel als locale ontbreekt) in plaats van de ruwe opgeslagen map. Zie Records.
  • Vertalingen zonder standaardtaal blijven bewaard. Een vertaalbare property of veld dat niet verplicht is en geen waarde voor de standaardtaal van de winkel heeft, bijvoorbeeld {"en": "Dusk"} met standaard nl, wordt zoals ingevuld opgeslagen in plaats van weggelaten; alleen een map waarvan alle waarden leeg zijn telt nog als leeg. Een verplicht veld geeft nog steeds 422. De richtext-allowlist bevat nu ook img met alleen src en alt (https:// of root-relatieve / bronnen; elke andere img wordt verwijderd). Zie Objects.

Opmerkingen

  • De recordvorm kreeg published_at (altijd) en slugs (typen met adressen) erbij. Behandel onbekende velden als additief, zoals altijd binnen /v1.

2026-10-11

Toegevoegd

  • capability (standard of made_to_measure) en capability_active (boolean) op het productobject van GET /v1/api/commerce/products en GET /v1/api/commerce/products/{id}. capability zegt hoe het product in een cart komt; capability_active is false zolang de module die het verkoopt (commerce voor standard, configurator voor made_to_measure) uitgeschakeld is. Zie Producten.
  • unit op het productobject (piece, kg, g, l, m, m2, m3, hour) en op elke orderregel (lines[].unit, de eenheid van het product op het moment van de order). Zie Producten en Bestellingen.
  • Productvelden compare_at_amount, barcode, weight_grams, requires_shipping, track_stock, returnable, lead_time_days, tags en social_image, en variantvelden barcode en weight_grams. Zie Producten.
  • GET /v1/api/redirects?path= geeft {data: {from_path, to_path, status}} terug voor een product- of categoriepad waarvan de slug is gewijzigd (404 als er geen is), en GET /v1/api/sitemap lijst elk actief product- en categoriepad op als {data: [{type, path, lastmod}]}. Beide nemen catalog:read of commerce:read en de catalog-module. Zie Redirects en sitemap.
  • Een code op het hoogste niveau op de 422 waarmee een cart-add een regel weigert (POST /v1/api/commerce/carts/{id}/lines): CAPABILITY_INACTIVE, INVALID_CONFIGURATION of NOT_PURCHASABLE, naast de ongewijzigde message en errors. Zie Cartregels en Errors en rate limits.
  • attributes.returnable en attributes.lead_time_days op elke orderregel die via de checkout of een geaccepteerde offerte is geplaatst; een made-to-measure-regel is nooit retourneerbaar. Zie Bestellingen.

Gewijzigd

  • Breaking: type is verwijderd uit het productobject. Gebruik in plaats daarvan capability, plus requires_shipping en track_stock: voormalige simple- en variable-producten zijn standard; configurable-producten zijn made_to_measure (en niet retourneerbaar); digital-, license- en subscription-producten zijn standard met requires_shipping: false en track_stock: false. Zie Producten.
  • Breaking: hoeveelheden zijn decimaal. quantity op cart-, order- en offerteregels, available bij beschikbaarheid, out_of_stock[].requested en .available bij checkout en offerte-acceptatie, en quantity in de stock.adjusted-webhook zijn JSON-getallen die decimalen kunnen hebben (0.75, 12). quantity op POST /v1/api/commerce/carts/{id}/lines en PATCH .../lines/{lineId} accepteert tot drie decimalen; een product met unit piece neemt nog steeds alleen gehele getallen (422 op quantity). Zie Cartregels, Carts, Bestellingen, Beschikbaarheid en Webhooks.
  • Breaking: de configurator-endpoints (GET /v1/api/commerce/configurator/products/{id}/schema, POST .../quote) hangen af van de configurator-module: zonder die module antwoorden ze 403 {"error":"Module not enabled: configurator"} (was catalog). Ze antwoorden 404 voor een product waarvan de capability niet made_to_measure is. Zie Configuratieschema, Prijsofferte en Hoe de configurator werkt.
  • Configurator-dimensies moeten gehele getallen zijn; een waarde met decimalen werd vroeger afgekapt en wordt nu geweigerd. Het quote-endpoint antwoordt 422 op dimensions.{key} ({Label} must be a whole number.) in zijn gewone validatie-envelope, zonder code op het hoogste niveau; een cart-add geeft dezelfde fout met code INVALID_CONFIGURATION. Zie Prijsofferte en Cartregels.
  • Breaking: components[].qty in de configuratiesnapshot op cart- en orderregels is een exacte decimale string ("2.16"), was een integer. Zie Carts en Bestellingen.
  • Breaking: POST /v1/api/commerce/carts/{id}/quote-request hoort bij de sales-module en antwoordt 404 {"error":"Module not enabled: sales"} wanneer de winkel sales heeft uitgeschakeld. Quote-triggers gaan alleen af wanneer sales is ingeschakeld en offerteaanvragen aan staan; anders is requires_quote altijd false en weigert de checkout nooit met QUOTE_REQUIRED. Zie Offerteaanvragen, Checkout en Errors en rate limits.
  • purchasability wordt ingesteld door de sales-module en is direct wanneer de merchant niets heeft ingesteld; het wordt ook gerapporteerd wanneer sales uitgeschakeld is. Een quote-product kan aan een cart worden toegevoegd: de cart meldt requires_quote: true met reden product_purchasability, de checkout weigert met 409 {"code": "QUOTE_REQUIRED", "reasons": [...]}, en de cart gaat via de offerteaanvraag. De instelling altijd offreren van een contactgroep is nu een sales-instelling; de reden customer_group is ongewijzigd. Zie Producten en Offerteaanvragen.
  • stock_status is untracked voor een product met track_stock: false en al zijn varianten, ook wanneer er voorraadrijen bestaan, op het productobject en op GET /v1/api/stock/availability en GET /v1/api/stock/products/{productId} (tracked: false). Zie Producten en Beschikbaarheid.
  • De checkout vereist shipping_method_id wanneer de cart een regel bevat waarvan het product requires_shipping: true heeft (was: een product van type simple, variable of configurable). Zie Checkout en Verzendmethoden.
  • Offerteregels die uit een cart zijn gemaakt hebben unit pcs voor een product dat per stuk verkocht wordt en anders de eenheid van het product (was null). Zie Offerteaanvragen en Offertetokens.
  • Een offerte accepteren in bestelmodus reserveert voorraad via de line handler van elk product tegen de geoffreerde prijs, zodat een made-to-measure-regel zijn componenten afboekt. Alleen een echt tekort weigert met OUT_OF_STOCK; een regel waarvan het product niet meer verkocht kan worden, wordt zonder voorraadmutatie omgezet. Zie Offertetokens.

Verouderd

  • QUOTE_NOT_ORDERABLE (422) op POST /v1/api/sales/quotes/by-token/{token}/accept en POST /v1/api/commerce/quotes/by-token/{token}/accept wordt niet meer teruggegeven: een offerte met fractionele regelhoeveelheden wordt omgezet in een bestelling met die hoeveelheden. Blijf de code tolereren, maar verwacht hem niet meer te zien. Zie Offertetokens.

Opmerkingen

  • Het /v1-oppervlak is geherstructureerd voordat een productieconsument ervan afhankelijk was, dus de breaking changes hierboven zitten binnen /v1 in plaats van onder een nieuwe URL-versie. Vanaf deze release zijn wijzigingen binnen /v1 weer additief.
  • Documentatiecorrecties, er is niets aan de API veranderd: de product- en categorie-endpoints accepteren catalog:read of commerce:read en hangen af van de catalog-module (de referentie noemde commerce:read en commerce); invoice_id op het orderobject en de volledige offerteaanvraag-respons (origin, intro, terms, acceptance_mode, pdf_url, signed_at, signed_pdf_url, en de regelvelden kind, unit, description, tax_rate, unit_price) zijn nu gedocumenteerd. Zie Producten, Categorieën, Bestellingen en Offerteaanvragen.

2026-09-17

Toegevoegd

  • discount_total (money) en discount_breakdown (array of null) op het orderobject dat POST /v1/api/commerce/carts/{id}/checkout, GET /v1/api/commerce/orders/{id} en GET /v1/api/commerce/my/orders teruggeven. discount_total.amount is de productkorting die bij het afrekenen is toegepast (0 als er geen was); discount_breakdown bevat per toegepaste korting één {discount_id, name, code, kind, amount}-item en is null als er geen korting gold. Beide zijn additief; er is geen bestaand veld gewijzigd. Zie Orders en Checkout.
  • message op de OUT_OF_STOCK-weigering van POST /v1/api/commerce/quotes/by-token/{token}/accept (alleen bestelmodus). De 409-body luidt nu {"code": "OUT_OF_STOCK", "message": "Insufficient stock.", "out_of_stock": [...]}; de out_of_stock-regels zijn ongewijzigd. Additief, en de envelope komt nu overeen met elke andere weigering op het endpoint (code + message). Zie Offertetokens.

Gewijzigd

  • POST /v1/api/commerce/carts/{id}/claim voegt nu elke andere active cart van de klant samen in de geclaimde cart, niet alleen de eerste die gevonden wordt. Elke samengevoegde cart krijgt status merged en geeft zijn voorraadreserveringen vrij, precies zoals voorheen; er blijft alleen geen verweesde actieve cart meer achter. Zie Een cart claimen en Storefront-gids.
  • De claim-merge neemt identiteit mee: de geclaimde cart behoudt zijn eigen email en metadata als die gezet zijn en neemt anders de waarde van de samengevoegde cart over, zodat een guest-e-mailadres dat vóór het inloggen is vastgelegd niet meer verloren gaat. Er is geen veld toegevoegd of van type veranderd op de cart-envelope. Zie Een cart claimen.

Opmerkingen

  • POST /v1/api/commerce/carts/{id}/lines: twee gelijktijdige identieke adds (zelfde product, variant en configuratie) lieten de verliezer voorheen falen met een 500. De verliezer telt zijn aantal nu op bij de winnende regel en geeft dezelfde 201-envelope terug als voorheen, nog steeds begrensd op max_quantity_per_line. Zie Cart-regels.
  • De verwerking van X-Customer-Token volgt nu consequent de gedocumenteerde regel "moet naar een klant herleiden, anders 401": een token dat voorbij zijn expires_at is, of ouder is dan het sessievenster van het platform, herleidt niet meer en kan dus geen klantgebonden cart claimen of afrekenen. Log opnieuw in voor een vers token. Zie Een cart claimen en Checkout.
  • POST /v1/api/customers/forgot-password antwoordt nog steeds 202 met dezelfde body voor bekende en onbekende adressen; het doet nu op beide paden ook evenveel queue-werk, zodat de responstijd niet langer verraadt of een account bestaat. Zie Wachtwoorden.
  • Een bogo-korting geeft zijn ontvangaantal nu één keer per kwalificatie in plaats van één keer per cart: het aantal afgeprijsde eenheden is get_quantity × floor(kwalificerend koopaantal ÷ minimaal koopaantal), begrensd door het aantal op de goedkoopste ontvangregel. discounts[].amount op de cart en discount_total op de order groeien mee; er is geen veld of enum-waarde gewijzigd. Zie Cart-kortingen.
  • Producten van het type configurable worden uitsluitend door de configurator-engine geprijsd; klantgroep- en staffelprijsrijen die na een typewijziging zijn achtergebleven verschijnen niet meer in price, quantity_tiers of de cart-regelprijs van zo'n product. Simpele producten zijn niet geraakt. Zie Producten.
  • POST /v1/api/commerce/quotes/by-token/{token}/accept: de QUOTE_NOT_OFFERED-weigering die tijdens het opbouwen van de bestelling ontstaat (een race met een andere accept van dezelfde offerte) draagt nu het gedocumenteerde bericht Quote is not awaiting a response (status: ...) in plaats van Quote cannot be accepted in its current status (...). Code en status zijn ongewijzigd. Zie Offertetokens.

2026-09-04

Gewijzigd

  • Een configurator-keuze waarvan de requires-regel niet vervuld kan worden door de andere gepubliceerde keuzes wordt nu weggelaten uit GET /v1/api/commerce/configurator/products/{id}/schema, in plaats van gepubliceerd te worden en daarna bij het opvragen van een quote afgewezen te worden. Het filteren werkt door, dus elke keuze die het schema toont kan geselecteerd en geprijsd worden. Zie Configuratieschema en Hoe de configurator werkt.
  • POST /v1/api/commerce/configurator/products/{id}/quote past dezelfde beschikbaarheidsregel toe: een keuze selecteren die het schema niet meer publiceert faalt met het bestaande generieke choices-bericht, en de choices.{choiceId}-berichten noemen nu alleen nog keuzes die beschikbaar zijn, zodat een gedeactiveerde keuze nooit in een foutmelding wordt prijsgegeven. Zie Prijsofferte.
  • Een prijsmatrix met een condition op een niet-beschikbare keuze wordt nu weggelaten uit price_matrices in plaats van vermeld te worden met een ingekorte conditions-array. Zo'n matrix kan nooit in aanmerking komen, dus de engine beschouwt de configuratie als not_priceable in plaats van erop terug te vallen. Zie Configuratieschema.

Opmerkingen

  • Documentatiecorrectie op beide configurator-endpoints: ze accepteren een API key met ofwel de scope catalog:read ofwel commerce:read (ongewijzigd sinds 2026-08-25), en hun module-gate is de catalog-module, dus de 403-body luidt {"error":"Module not enabled: catalog"}. De referentie noemde voor beide commerce:read en commerce. Er is niets aan de API veranderd. Zie Prijsofferte, Configuratieschema en Hoe de configurator werkt.

2026-08-26

Toegevoegd

  • Customer-endpoints worden nu ook aangeboden op /v1/api/contacts/*; de eerdere /v1/api/customers/* paden blijven bestaan als permanente alias met identieke request- en responsevormen. Nieuwe API-key scopes contacts:read en contacts:write zijn beschikbaar; bestaande customers:read / customers:write keys blijven ongewijzigd werken. Zie Contactaccounts.
  • Het contactprofiel (GET/PATCH /v1/api/contacts/me, ook .../customers/me) bevat nu kind (person of company), company_name, registration_number, vat_id, external_id, en display_name. Deze velden zijn alleen-lezen via de storefront API. email kan nu null zijn voor elk door de handelaar aangemaakt contact; registratie via de storefront vereist het nog steeds. Zie Profiel.
  • Het veld type van een adres accepteert een nieuwe waarde, work, naast shipping en billing. Zie Adressen.
  • contact_id op bestelling- en merchant-offerteobjecten: dezelfde waarde als customer_id, onder de canonieke naam vanaf nu. customer_id verandert niet en blijft bestaan. Het token-geadresseerde offerteobject bevat geen van beide. Zie Bestellingen en Checkout.
  • De offertetoken-endpoints worden nu ook aangeboden op /v1/api/sales/quotes/by-token/{token} (GET, /accept, /decline). /v1/api/commerce/quotes/by-token/* blijft identiek en permanent werken; beide paden accepteren een key met sales:read/sales:write of commerce:read/commerce:write. Zie Offertetokens.
  • GET /v1/api/{sales,commerce}/quotes/by-token/{token} retourneert origin, intro, terms, scope_items[], acceptance_mode, pdf_url, signed_at en signed_pdf_url; elk item van lines[] krijgt er kind, unit, description en tax_rate bij. Zie Offertetokens.
  • POST /v1/api/{sales,commerce}/quotes/by-token/{token}/accept accepteert een optionele body-parameter signature — een PNG-data-URL van hoogstens 2 MB, verplicht wanneer de acceptance_mode van de winkel signature is — en retourneert signed_pdf_url. Nieuwe foutcodes SIGNATURE_REQUIRED (422), SIGNATURE_INVALID (422), QUOTE_NOT_ORDERABLE (422), ALREADY_CONVERTED (409) en QUOTE_EXPIRED (410). Zie Offertetokens.
  • Nieuwe scopes sales:read en sales:write, overal geaccepteerd waar commerce:read/commerce:write wordt geaccepteerd op de offertetoken-endpoints. Zie Authenticatie.
  • De status van een offerte kan nu draft zijn: door de merchant geschreven offertes bestaan voordat ze worden verstuurd. Een draft heeft geen nummer en is nooit bereikbaar via token.

Gewijzigd

  • Customer-endpoints vereisen niet langer dat een module is ingeschakeld; het 403-antwoord Module not enabled: customers kan niet meer voorkomen. Zie Klantaccounts en Errors en rate limits.
  • Offerteregel quantity (merchant-offertes en het token-geadresseerde offerteobject) wordt nu gedocumenteerd als number in plaats van integer: productregels melden nog steeds altijd een heel getal, maar dienst- en materiaalregels kunnen nu een fractionele hoeveelheid bevatten (uren, meters). Elke voorheen geheeltallige waarde blijft exact hetzelfde serialiseren. Zie Offerteaanvragen en Offerte-tokens.
  • Accept en decline op de offertetoken-endpoints zijn beperkt tot 30 requests per minuut, gesleuteld op het IP-adres van de aanroeper. Zie Errors en rate limits.

Opmerkingen

  • lines[].quantity op de offertetoken-endpoints was altijd al een JSON-getal en is dat nog steeds; het mag nu fractioneel zijn (twee decimalen). Er is geen veld verwijderd, hernoemd of van type veranderd.

2026-08-25

Gewijzigd

  • Publieke catalogusroutes (/v1/api/commerce/products, /v1/api/commerce/categories, /v1/api/commerce/configurator/*) accepteren nu een API key met ofwel de scope catalog:read ofwel commerce:read. Bestaande keys blijven ongewijzigd werken. Zie Authenticatie.
  • Nieuwe API-key scopes catalog:read en catalog:write zijn beschikbaar voor winkels met de Catalog-module ingeschakeld. Scope-namen van alle andere modules zijn ongewijzigd.

Opmerkingen

  • In deze release is geen requestparameter, responseveld, statuswaarde of error envelope gewijzigd.