Changelog
Changelog
Gecureerde highlights van de laatste releases. Voor volledige API-versies en het breaking-change-beleid, zie de API-versiebeheer & LTS-beleid.
Gedetailleerde wijzigingen aan afzonderlijke endpoints: OpenAPI-specificatie en interactieve API-referentie.
2026-08 · Keyset-cursor nu op alle lijst-endpoints
- Fix: De cursor-fix voor
GET /v1/codesenGET /v1/dpp(zie hieronder) is nu uitgerold naar alle overige lijsten met cursor-paginering:GET /v1/qr-codes/:id/comments,/v1/workspaces,/v1/gs1/identifiers,/v1/members,/v1/audit-logs,/v1/admin/orgs,/v1/admin/usersen/v1/webhooks/:id/deliveries. Deze bladerden allemaal uitsluitend op basis vancreated_at; rijen met een identieke tijdstempel (audit-vermeldingen van een batch-operatie, webhook-retries, importen van leden) konden op de volgende pagina verloren gaan. De sorteersleutel is nu overal de tuple(created_at, id). - API-wijziging: Op deze lijsten is
meta.pagination.next_cursorvanaf nu ook een opake waarde (base64url) in plaats van een kale tijdstempel; onleesbare cursors retourneren400, oude tijdstempel-cursors worden tijdelijk nog geaccepteerd. UitzonderingGET /v1/webhooks/:id/deliveries: daar blijftnext_cursorde ID van de laatste aflevering (onbekende ID → eerste pagina). Clients dienext_cursorongewijzigd terugsturen — Dashboard, CLI, SDKs, MCP —, hoeven niets te wijzigen. - Impact: Geen migratie vereist. Als je in een van deze lijsten (bijv. het audit-log of afleverlogboek in het Dashboard) bij het bladeren items hebt gemist: ze waren nooit weg — de lijsten tonen ze vanaf nu volledig.
2026-08 · Tijdstempels na bewerken en verwijderen weer OpenAPI-conform
- Fix: Na
PATCH /v1/codes/{id}kwamupdated_atterug in SQLite-formaat zonder tijdzone (2026-08-17 09:00:00), hoewel de OpenAPI-specificatieformat: date-timebelooft en het aanmaken (POST) de ISO-tijdstempel (2026-08-17T09:00:00.000Z) levert. Hetzelfde gold voordeleted_at/updated_atbij soft-delete en voor de bewerk-/verwijderpaden van API-keys, organisaties, workspaces, leden, reacties en webhooks, evenals voorlast_used_atvan API-keys enlast_triggered_atvan webhooks. Alle schrijfpaden stempelen nu ISO 8601 (UTC,TenZ). - Impact: Clients die
updated_atparseren metnew Date(...)(SDKs, CLI, Dashboard), lazen het formaat met de spatie als lokale tijd — de CLI toonde bij eenmaal bewerkte codes de tijd verschoven met de lokale offset (Wenen: −2 u). Dit is opgelost. Daarnaast normaliseert een datamigratie reeds opgeslagen waarden in het oude formaat naar ISO, zodat sortering en vergelijkingen in gemengde datasets kloppen. Geen wijziging in veldnamen of antwoordstructuur. - Achtergrond: Zelfde foutklasse als de twee fixes hieronder (API-key-verloop, re-scan-cutoff): SQLite’s
datetime('now')schrijftYYYY-MM-DD HH:MM:SS, alle overige schrijvers ISO 8601. Een guard-test in de broncode voorkomt in de toekomst nieuwe gevallen.
2026-08 · Lijst-paginering verliest geen batch-codes meer
- Fix:
GET /v1/codesenGET /v1/dpppagineerden uitsluitend op basis vancreated_at. Codes uitPOST /v1/codes/batch,POST /v1/dpp/batchen de CSV/XLSX-import delen echter één tijdstempel — zodra een batch groter was danlimit(standaard 20), leverde de tweede pagina de resterende rijen met dezelfde tijdstempel niet meer. De codes bestonden en waren viaGET /v1/codes/:idbereikbaar, maar doken in de lijst (Dashboard, CLIqr3 list, SDKs, MCP) nooit op. De cursor is nu een keyset over(created_at, id). - API-wijziging:
meta.pagination.next_cursoris vanaf nu een opake waarde (base64url) in plaats van een kale tijdstempel. Wie de cursor ongewijzigd als?cursor=retourneert — zoals het Dashboard, de CLI, alle SDKs en de MCP-server dat doen — hoeft niets te veranderen. Oude tijdstempel-cursors worden tijdelijk nog geaccepteerd; onleesbare cursors leveren nu een400op in plaats van stilzwijgend de eerste pagina te tonen. - Impact: Voor wie na een batch-import minder codes in de lijst zag dan er waren aangemaakt: de codes zijn nooit weggeweest — de lijst toont ze vanaf nu volledig. Geen migratie nodig.
2026-08 · Beveiligings-re-scans worden weer om de 24 uur uitgevoerd
- Fix: Het periodiek re-scannen van doel-URL’s en landingspagina-links (Google Web Risk) sloeg codes over waarvan de laatste scan op dezelfde kalenderdag viel als de 24-uurs-cutoff — afhankelijk van het tijdstip werd de re-scan met maximaal een extra dag vertraagd. De cutoff wordt nu berekend in hetzelfde ISO-formaat waarin de scan-tijdstempels zijn opgeslagen.
- Impact: Een doel-URL die na de laatste scan als onveilig wordt geclassificeerd, leidt nu weer binnen het gedocumenteerde 24-uursvenster tot het automatisch pauzeren van de code. Geen wijzigingen in de API of het antwoordformaat.
2026-08 · API-keys verlopen op het verlooptijdstip
- Fix: Een API-key waarvan
expires_atop dezelfde dag viel, werd tot middernacht UTC geaccepteerd. De vervaltijd wordt nu vergeleken als een tijdstempel in plaats van een string — een verlopen key retourneert nu direct401. - Achtergrond:
expires_atwordt opgeslagen als een ISO-tijdstempel (2026-08-14T09:00:00Z), de vergelijkingszijde leverde het formaat met een spatie (2026-08-14 09:00:00). De ruwe stringvergelijking klopte daarom alleen zolang de datum al verschilde. - Impact: Geen migratie vereist, het antwoordformaat van
GET /v1/api-keysblijft ongewijzigd. Onleesbare vervalwaarden worden nu als verlopen beschouwd in plaats van als geldig.
2026-08 · API-referentie: Tenant-beheer gedocumenteerd
- OpenAPI: De specificatie — en daarmee de interactieve referentie — documenteert nu organisaties (incl.
GET /v1/organizations/usage), workspaces, leden & rollen en audit-logs. - Billing: Het tarievenoverzicht (
GET /v1/billing/plans) is openbaar; checkout (POST /v1/billing/checkout) en Stripe-klantenportaal (GET /v1/billing/portal) zijn aangemerkt als endpoints voor organisatiebeheerders. - Scan-export: Scanstatistieken (
GET /v1/codes/{id}/scans) en de ruwe data-export (…/scans.csv,…/scans.xlsx) zijn volledig gedocumenteerd — inclusief AVG-opmerking: Deip_hashis nooit in de export opgenomen. - Foutgedrag: Ook nieuw gedocumenteerd is het
400-antwoord van de request-validatie: De body is de ruwe Zod-fout, geen RFC-7807-probleemdocument — deze wordt desondanks geleverd met de Content-Typeapplication/problem+json.
2026-08 · Openbare bestandslinks kopiëren
- Dashboard: Openbare bestanden op de detailpagina van de code hebben nu een knop die hun openbare link naar het klembord kopieert – direct bruikbaar als doel-URL van een QR-code wanneer een scan direct een specifiek document moet openen in plaats van de landingspagina met de bestandslijst.
- API: De bestands-endpoints (
/v1/files) leveren daarnaastpublic_url. Dit veld is alleen ingevuld bij bestanden metvisibility: public– privébestanden krijgen geen openbaar adres. - Gedrag: De link vereist geen aanmelding en opent het bestand direct in de browser. Het vervangen van het bestand laat de link ongewijzigd, waardoor een daarop gedrukte code geldig blijft. Details: Bestanden & datasheets.
2026-07 · Teamrollen: Bewerker zonder verwijderen & admin-facturering
- Nieuw: Lidrol Bewerker (zonder verwijderen) — maakt QR-codes, bestanden en Digital Product Passports aan en bewerkt deze, maar kan niets verwijderen en geen API-keys aanmaken. Alle destructieve endpoints controleren de rol aan de serverzijde (
403). - Facturering: Abonnementsupgrades en het Stripe-klantenportaal (
POST /v1/billing/checkout,GET /v1/billing/portal) zijn nu voorbehouden aan organisatiebeheerders — alle andere rollen zien een alleen-lezen abonnementsverzicht. - Dashboard: Acties die de eigen rol niet toestaat, worden verborgen: een Lezer ziet bijvoorbeeld geen knoppen voor het aanmaken, bewerken of verwijderen; lijsten, downloads en statistieken blijven zichtbaar. Details: Team & rollen.
2026-06 · Externe links op de code-landingspagina
- Landingspagina: De door qr3 gehoste landingspagina van een code kan nu externe, zelfgehoste links (
{ label, url }) tonen – naast of in plaats van geüploade bestanden, bijvoorbeeld voor datasheets op de eigen website. - API:
POST/PATCH /v1/codesaccepteren eenlinks-array (0–20 items,http(s), ≤ 2048 tekens). Elke URL wordt gecontroleerd met Google Web Risk; een onveilige URL retourneert422. Een lege array verwijdert alle links. - Dashboard: Links toevoegen, sorteren en verwijderen op de detailpagina van de code.
- Beveiliging: Gerenderde links blijven XSS-veilig (escaped, alleen
http(s)) en de pagina behoudt zijnnoindex-header.
2026-04 · Dashboard-statistieken per QR-code
- Dashboard: De statistiekenknop in de QR-codelijst opent nu de statistiekenpagina van de betreffende QR-code onder
/dashboard/codes/{id}. - Routing: De alias
/dashboard/codesstuurt nog steeds door naar/dashboard, maar vangt geen detailroutes zoals/dashboard/codes/{id}meer af. - API: De detailpagina laadt de QR-code direct via
GET /v1/codes/:id; hierdoor is deze niet meer afhankelijk van paginalimieten van de lijst. - Tests: Regressietests dekken de alias-redirect en het direct laden van de code af.
2026-04 · Dashboard-verwijderdialoog voor QR-codes
- Dashboard: De prullenbak in de QR-codelijst opent nu een eigen React-dialoogvenster in plaats van een native browser-popup.
- Feedback: Na het verwijderen verschijnt een toast-melding voor succes of fouten.
- Tests:
packages/dashboard/tests/dashboard.test.tsvoorkomt regressies opconfirm()in de QR-code-verwijderflow.
2026-04 · Dashboard-kortelinktest voor dynamische QR-codes
- Dashboard: Shortcodes in de QR-codelijst zijn nu direct klikbaar als externe redirect-links. Het externe-link-icoon naast bijv.
wu3qaaopenthttps://qr3.app/{shortCode}in een nieuw tabblad. - i18n: Tooltip-teksten voor Duits en Engels toegevoegd.
- Tests:
packages/dashboard/tests/dashboard.test.tsbeschermt link-href, nieuw tabbladgedrag,noopener noreferreren icoon tegen regressies.
2026-04 · Redirect-Worker-route voor dynamische QR-codes
- Fix: Dynamische QR-codes onder
https://qr3.app/{shortCode}worden weer verwerkt door de redirect-Worker. De productie-route gebruikt nuqr3.app/*, omdat Cloudflare Worker-routes geen:code-padparameters ondersteunen. - Beveiliging: Niet-overeenkomende paden worden doorgestuurd naar de landing-origin, zodat normale pagina’s zoals
/de/pricingniet worden geblokkeerd door de redirect-Worker. - Tests:
packages/redirect/tests/unit/redirect.test.tscontroleert de wildcard-route, shortcode-verwerking en origin-pass-through.
2026-04 · Workspace DPP-scanoverzicht (Q3.4.2)
- Nieuw:
GET /v1/workspace/stats/dpp?days=30— aggregeert alledpp_scansvan de API-key-workspace (active_dpps,scans_by_day,top_dppsmet productnaam/categorie). - Dashboard: Kaart op de startpagina (
/dashboard) met 30-dagen staafdiagram + toplijsten — parallel aan de QR-codekaarten. - Openbaar: Marketing-shortlink
GET /dpp/dpp_<id>(één segment) voor live demo’s, parallel aan/dpp/{gtin}/{serial}.
2026-04 · DPP-scanstatistieken (Q3.4.1)
- Nieuw:
GET /v1/dpp/:id/stats?days=30— geaggregeerde scans van de openbare GS1-resolver per DPP. Velden:total_scans,period_scans,scans_by_day,top_countries,top_devices,top_representations. - Nieuw: Tabel
dpp_scans(migratie0011) — gescheiden vanscans(redirect-Worker). IP-adressen worden gehasht met een dagelijks roterende salt, ruwe IP’s bereiken D1 nooit. - Dashboard: Mini-grafiekkaart (SVG, geen grafiekbibliotheek) op
/dashboard/dpp/:dppIdmet 30-dagen balken + top-3 breakdowns. Empty state zodra een DPP live is maar nog geen scans heeft gehad.
2026-04 · Live EU-compliance-simulator (Q3.3.7)
- Nieuw:
POST /v1/dpp/:id/validate-update— simuleert gedeeltelijke updates stateless (status, marktlijst, …) zonder persistentie. Antwoord bevateu_compliance+preview.changed_fields. - Dashboard: Simulatorkaart in het DPP-detail (
/dashboard/dpp/:dppId) — chips voorDE/AT/FR/IT/ES/NL+ custom, status-dropdown, Preview EU impact / Save changes / Reset. Non-blocking via RemixuseFetcher. - Beveiliging: Uitbestede simulator-helpers (
readUpdatePatchFromForm,marketCountriesKey) + 18 nieuwe unit-tests; Bugfix: een enkele niet-ISO-invoer wist de marktlijst niet meer.
2026-04 · Live EU-compliance-preview in het aanmaakformulier (Q3.3.6)
- Gewijzigd:
POST /v1/dpp/validatelevert daarnaasteu_compliance— dezelfde validator alsGET /v1/dpp/:id/eu-compliance, stateless vóór het opslaan. - Dashboard: Preview onder het bestaande validatiepaneel + nieuwe Save-Guard-banner vóór de submit-knoppen als er fouten/waarschuwingen openstaan (i18n-pluralisatie DE/EN).
2026-04 · EU-validator + textiel-UI (Q3.3.4 + Q3.3.5)
- Nieuw: EU-compliance-validator met 5 textielregels (
TEXTILE_AGEC_REQUIRED,TEXTILE_MICROPLASTICS_CONSISTENCY,TEXTILE_SVHC_THRESHOLD,TEXTILE_GREENWASHING,TEXTILE_ESPR_READY). - Nieuw:
GET /v1/dpp/:id/eu-compliancemetcompliant/espr_ready/issues[]/summary. - Dashboard: EU-compliance-sectie in het DPP-detail (samenvattingstegels, gegroepeerde issue-kaarten, ESPR-Ready-badge in de header).
2026-04 · Textiel-DPP-schema (Q3.3.1–Q3.3.3)
- Nieuw: Categorie
textilemet verplichte AGEC-keten (weven/breien → verven/bedrukken → confectie), per vezelorigin_country+recycled_pct,svhc_substances[], ESPR-opt-in (PEF, levensduur, Recyclability). - Nieuw: Basisveld
market_countries: string[](ISO 3166-1 alpha-2) op alle DPP-categorieën — stuurt FR-specifieke AGEC-regels en de Franse verplichte consumentennotitie aan. - Nieuw: Consumenten-HTML-template met AGEC-microplastic-waarschuwingsbox, herkomstketen in 3 stappen (vlag-pills), SVHC-lijst, Durability- en Recyclability-sectie.
- Migratie:
0010_dpp_market_countries(D1).
2026-04 · DPP bulk-import (Q3.2.1–Q3.2.5)
- Nieuw:
POST /v1/dpp/importaccepteert CSV en XLSX (Worker-compatibel via SheetJSxlsx, ~283 KB gzip-bundel). - Geschaald: abonnement-gebaseerde limiet (Free 100 → Enterprise 10k) + chunked
db.batch()à 100 + 5 MB body-limiet. - Nieuw: Foutrapportage als CSV in het
errors_csv-veld van het 201-antwoord;GET /v1/dpp/import/templates/:category?format=csv|xlsxlevert kant-en-klare sjablonen voor batterij en textiel. - Dashboard: Drag-and-drop-upload onder
/dashboard/dpp/importmet template-proxy en inline CSV-download.
Non-breaking — LTS-uitbreidingen
Alle bovengenoemde wijzigingen zijn additief:
- Bestaande
POST /v1/dpp/validate-clients negeren het nieuweeu_compliance-veld zonder wijzigingen. - Bestaande
battery-flows zijn ongewijzigd. market_countriesis optioneel en heeft als standaardwaarde[].
Zie API-versiebeheer voor het breaking-change-beleid.