Ga naar inhoud

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-10 · Kleuren in het Dashboard

  • Nieuw: de detailpagina van een code heeft de kaart Kleuren: voorgrond en achtergrond als hexwaarde, transparante achtergrond, voorbeeld via de echte afbeeldingsroute. De controle is die van PATCH /v1/codes/{id}: kritieke paren vragen een bevestiging, paren onder 1,5:1 kunnen niet worden opgeslagen.
  • Terugzetten naar zwart op wit levert in alle vier de formaten weer dezelfde bytes als zonder kleuren. Codes zonder kleuren veranderen niet.
  • Transparante codes verschijnen in alle voorbeelden van het Dashboard op een dambordpatroon. Details: Kleuren in het Dashboard.

2026-10 · Kleuren in PDF en EPS

  • Nieuw: qr.pdf en qr.eps tekenen fg en bg, als parameter en als opgeslagen kleuren. Zwart en elke grijstint worden als grijswaarde geschreven (alleen zwartplaat), elke andere kleur als CMYK in hele percentages, bijvoorbeeld 1F4E79 als C74 M36 Y0 K53.
  • Achtergrond zoals bij SVG: Zodra een kleur is gekozen, ligt er een dekkend vlak achter de code en de bijbehorende rustzone, wit of bg. bg=transparent tekent er geen. Titel blijft zwart, logoplaat wit.
  • Geen wijziging zonder kleuren: Zonder fg/bg en zonder opgeslagen kleuren leveren beide formaten dezelfde bytes als voorheen. De PDF van een productpaspoort blijft ongekleurd.
  • Beperkingen: geen ICC-profiel, geen PDF/X. Details: Kleuren bij het afdrukken.

2026-09 · Statische codes zonder omleidingscache, gelijktijdig verwijderen

  • Gewijzigd: Statische codes bevinden zich niet meer in de omleidingscache. Een aanroep van hun korte link (redirect_url) leest altijd de actuele status. Na DELETE /v1/account leidt een statische code daarom bij de volgende aanroep niet meer om, voorheen tot wel 24 uur lang. Ook een tariefwijziging heeft bij statische codes direct effect. Dynamische codes worden bij het verwijderen van het account net als voorheen opgeruimd. Gedrukte statische codes bevatten hun bestemming direct en zijn niet getroffen.
  • Opgelost: Twee gelijktijdige DELETE /v1/codes/:id-aanroepen op dezelfde code leveren eenmaal 200 en eenmaal 404 op. De webhook qr.deleted wordt precies één keer verzonden, voorheen twee keer.
  • Nieuw: POST en DELETE /v1/codes/:id/logo antwoorden met 409 (errors/conflict) als een ander verzoek het logo van dezelfde code gelijktijdig heeft gewijzigd, en wijzigen dan niets. Voorheen kon een geüpload logo ongebruikt opgeslagen blijven. Twee gelijktijdige DELETE /v1/codes/:id/logo-aanroepen leveren beide 200 op. Details: Logo.

2026-09 · TypeScript-SDK 1.2.0

  • Nieuw in @qr3/sdk 1.2.0: client.codes.update() accepteert appearance en levert de resultaten van de contrastcontrole als issues in het resultaat; zonder bevindingen ontbreekt het veld. client.codes.imageUrl() accepteert fg, bg en ecc. Elke code die de API retourneert (get, list, create, update, batchCreate) bevat title en appearance.
  • Bijwerken: npm install @qr3/sdk@latest. Python, Go en PHP volgen. Details: SDK’s & CLI en Kleuren opslaan bij de code.

2026-09 · Gewijzigde vervaltijd is binnen ongeveer een minuut van kracht

  • Opgelost: Een PATCH op expires_at schoont nu de redirect-cache van de code op. De nieuwe vervaltijd is hiermee binnen ongeveer een minuut van kracht, in plaats van pas tot 24 uur later. Voorheen leverde een verwijderde of verschoven vervaltijd tot 24 uur lang nog 410 op, en een op ‘nu’ gezette vervaltijd liet de redirect tot 24 uur lang doorlopen.
  • Opgelost: Een met expires_at aangemaakte code verloopt ook op tijd als de vervaltijd binnen de eerste 24 uur valt.
  • Opgelost: Als een code wordt verwijderd terwijl het logo ervan wordt geüpload of verwijderd, antwoorden POST en DELETE /v1/codes/:id/logo met 404 en wijzigen ze de verwijderde code niet, net zoals PATCH al deed.
  • Opgelost: Ook een statische code waarvan de korte link (redirect_url) is aangeroepen, bevindt zich in de redirect-cache. Wijzigen, pauzeren en verwijderen schonen deze nu ook op, niet alleen bij dynamische codes.

2026-09 · Kleuren bij de code opslaan

  • Nieuw: PATCH /v1/codes/:id accepteert appearance met foreground_color en background_color (#RRGGBB, de achtergrond ook transparent). qr.svg en qr.png tekenen de opgeslagen kleuren zonder parameters; een parameter zoals ?fg=000000 heeft nog steeds voorrang. PDF en EPS tekenen kleuren pas in een latere release.
  • Contrastcontrole: Een paar onder 1,5:1 resulteert in 422. Zwakkere paren worden opgeslagen en in meta.issues als warning of critical gemeld; een transparante achtergrond is altijd critical, nooit geblokkeerd.
  • Samenvoegen: Weggelaten velden behouden hun opgeslagen waarde, null zet deze terug. Gelijktijdige wijzigingen aan dezelfde code overschrijven elkaar niet meer.
  • In elk code-antwoord: appearance staat in elk code-antwoord en in de webhooks qr.created en qr.updated. POST, de batch en de import weigeren dit met 422.
  • Geen wijziging voor bestaande codes: Zonder opgeslagen kleuren levert elke code dezelfde bytes als voorheen. Details: Kleuren bij de code opslaan.

2026-09 · Kleuren en foutcorrectie als parameters van de afbeeldingsroutes

  • Nieuw: Alle vier afbeeldingsroutes accepteren fg (voorgrondkleur), bg (achtergrondkleur of transparent) en ecc (foutcorrectie L, M, Q, H). SVG en PNG tekenen de kleuren; PDF en EPS accepteren ze, maar tekenen ze pas in een latere release. ecc is effectief in alle vier de formaten, een logo dwingt nog steeds H af.
  • Transparant: bg=transparent levert een SVG zonder achtergrond en een PNG met een echt alfakanaal. De ondergrond moet licht zijn en de rustzone vrijlaten.
  • Nooit een fout: Ongeldige waarden worden genegeerd; de afbeelding wordt dan exact zo geleverd als zonder parameters.
  • Caching: Elke effectieve parameter levert Cache-Control: public, max-age=300 in plaats van de 24 uur van de standaardafbeelding.
  • Geen gedragswijziging zonder parameters: Elke bestaande code levert in alle vier de formaten dezelfde bytes als voorheen.
  • Beschikbaar in elk tarief. Details: Kleuren en foutcorrectie.

2026-09 · PDF en EPS sluiten het logo nu ook in

  • Uitbreiding: qr.pdf en qr.eps sluiten een ingesteld logo nu net zo in als qr.svg en qr.png (zie hieronder) — als ingebedde afbeelding (PDF via een Image XObject, EPS via een afbeeldingsdictionary in PostScript, daar alleen met %%LanguageLevel: 3), gecentreerd op hetzelfde vlak, met dezelfde verhoging van de foutcorrectie naar H. Beide formaten leverden het logo voorheen niet mee; dit is nu opgelost.
  • Caching: Met een ingesteld logo leveren nu ook qr.pdf en qr.eps Cache-Control: public, max-age=300 in plaats van de anders gebruikelijke 24 uur — net als SVG/PNG.
  • Fallback: Als de logo-afbeelding zelf niet kan worden verwerkt (bijvoorbeeld een beschadigd opgeslagen object), blijft die foutcorrectie H, maar blijft het gereserveerde vlak leeg in plaats van een afbeelding — nooit een serverfout.
  • Geen gedragsverandering zonder logo: Codes zonder logo leveren qr.pdf/qr.eps nog steeds byte-identiek, met een ongewijzigde 24-uurs cache.
  • Details en printinstructies: Logo in QR-code.

2026-09 · Logo in QR-code — Dashboard en API

  • Nieuw: Een code kan nu een logo in het midden bevatten. In het Dashboard maakt de detailpagina van een code een eigen logo-kaart aan: Logo selecteren, bij de eerste keer toevoegen of bij het verwijderen een waarschuwing met een verplicht vinkje bevestigen (beide wijzigen het puntenpatroon), daarna Logo uploaden/Logo verwijderen. Een Vervangen heeft geen bevestiging nodig — alleen de afbeelding verandert, niet het patroon. De rol viewer ziet de status en het voorbeeld, maar geen van de drie acties.
  • API: POST /v1/codes/{id}/logo (multipart-veld file; PNG, JPEG of WebP, maximaal 1 MB, SVG wordt geweigerd) maakt een logo aan of vervangt dit; DELETE /v1/codes/{id}/logo verwijdert het weer en is idempotent. Een te grote afbeelding levert 413 op, een niet-ondersteund formaat 422, de rol viewer 403.
  • Weergave: qr.svg en qr.png sluiten het logo in als daadwerkelijke pixels (512 × 512, genormaliseerd) en verhogen daarvoor de foutcorrectie naar H. Toevoegen en verwijderen wijzigen hiermee het puntenpatroon (M ↔ H), vervangen niet. qr.pdf en qr.eps sluiten geen logo in en blijven ongewijzigd. Een reeds gedrukte code blijft in elk geval werken, omdat het gecodeerde doel niet afhankelijk is van het logo — alleen wie de (nieuwe) logo-uitstraling ook op het materiaal wil zien, moet opnieuw drukken, en daarbij oude en nieuwe drukbestanden niet mengen.
  • Caching: Met een ingesteld logo leveren qr.svg and qr.png Cache-Control: public, max-age=300 in plaats van de anders gebruikelijke 24 uur. Details, limieten en printinstructies: Logo in QR-code.

2026-08 · operationId voor alle 75 operaties, en een gebruiksgids voor agenten

  • Aanvulling: Alle 75 operaties van de OpenAPI-specificatie hebben nu een operationId — listCodes, createCode, getCodeStats, archiveWorkspace enzovoort. Tot nu toe ontbrak dit veld overal, waardoor elke generator de methodenaam moest afleiden uit de HTTP-methode en het pad (postV1Codes). Dergelijke namen zijn afhankelijk van het pad en veranderen bij elke wijziging van het pad. De naamgevingsstructuur is op alle 75 plaatsen hetzelfde: list/get/create/update/replace/delete, anders het werkwoord van de functionele handeling (validateDpp, registerGs1Identifier, pingWebhook). Het werkwoord volgt de functionaliteit, niet de HTTP-methode — archiveWorkspace is een DELETE, importDpps is een POST.
  • Impact op zelfgegenereerde clients: Wie zijn SDK genereert uit de specificatie, krijgt bij de volgende run te maken met hernoemde methoden (postV1Codes → createCode). Dit is het doel van de wijziging, maar het is een hernoeming in externe code — vandaar dat dit hier expliciet wordt vermeld. Paden, parameters, antwoordformaten en statuscodes zijn ongewijzigd; de officiële SDKs, CLI en MCP-server zijn niet getroffen.
  • Startpunt voor agenten: https://qr3.app/llms.txt heeft een sectie ## When to use qr3.app — zes taken in plaats van zes functies, plus een zin die aangeeft waarvoor qr3.app niet het juiste gereedschap is. Dezelfde informatie levert de MCP-server nu bij de handshake: initialize tegen https://mcp.qr3.app/mcp antwoordt met een veld instructions (voorheen: elf tools zonder enige toelichting). Er is ook opgenomen welke aanroepen een API-sleutel nodig hebben — initialize en tools/list niet, elke tools/call wel.
  • Correcties in llms.txt: Vier vermeldingen zijn getoetst aan de productieomgeving en bleken niet te kloppen: (1) de Python-SDK qr3app is niet gepubliceerd op PyPI — de pip install-regel is zonder vervanging verwijderd; (2) Accept: text/markdown geldt voor de startpagina en de blog, niet voor elke server-side gerenderde pagina; (3) het contactadres is [email protected], ook in de JSON-LD van de startpagina; (4) /de/security/ is een omleiding naar een anker, geen eigen pagina. Nieuwe link: docs.qr3.app/de/skills/.
  • Borging: Drie asserties in openapi-spec.test.ts — volledigheid, uniciteit, lowerCamelCase —, elk gecontroleerd met een mutatie. Negen extra tests leggen de vier gecorrigeerde vermeldingen vast, zodat ze niet terugkeren.
  • Bekende beperking: Twee van de 75 operaties zijn beschreven in de specificatie, maar antwoorden met 404 (GET /v1/codes/{id}/stats en GET /v1/account). Ze hebben hier een naam gekregen en verliezen deze weer zodra is besloten of ze worden gebouwd of geschrapt.

2026-08 · Downgrades van abonnementen en opzeggingen zijn nu ook van invloed op de limieten

  • Fix: De Stripe-webhook schreef bij een abonnementswijziging en opzegging alleen de naam van het abonnement, niet de vier limietkolommen van de organisatie (max_workspaces, max_members, max_dynamic_codes, max_scans_per_month). Omdat de handhaving het maximum neemt van de opgeslagen waarde en de baseline van het abonnement, behield een gedowngrade of opgezegde organisatie haar oude, hogere limieten — de downgrade had geen effect. De webhook stelt de kolommen nu bij elke echte abonnementswijziging in op de baseline van het nieuwe abonnement en bij opzegging op de Free-baseline — precies zoals het admin-beheer dat al doet.
  • Gedrag: Routine-updates van het abonnement (verlenging, betaalmethode, proration) raken de limieten nog steeds niet aan; individueel toegekende hogere limieten blijven ongewijzigd behouden. Pas bij een echte abonnementswijziging worden ze teruggezet naar de baseline van het nieuwe abonnement.
  • Impact: Geen wijziging in de API of het antwoordformaat. Organisaties waarvan de downgrade vóór deze fix plaatsvond, behouden de oude waarden totdat de volgende abonnementswijziging van kracht wordt of de support deze aanpast.

2026-08 · Keyset-cursor nu op alle lijst-endpoints

  • Fix: De cursor-fix voor GET /v1/codes en GET /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/users en /v1/webhooks/:id/deliveries. Deze bladerden allemaal uitsluitend op basis van created_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_cursor vanaf nu ook een opake waarde (base64url) in plaats van een kale tijdstempel; onleesbare cursors retourneren 400, oude tijdstempel-cursors worden tijdelijk nog geaccepteerd. Uitzondering GET /v1/webhooks/:id/deliveries: daar blijft next_cursor de ID van de laatste aflevering (onbekende ID → eerste pagina). Clients die next_cursor ongewijzigd 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} kwam updated_at terug in SQLite-formaat zonder tijdzone (2026-08-17 09:00:00), hoewel de OpenAPI-specificatie format: date-time belooft en het aanmaken (POST) de ISO-tijdstempel (2026-08-17T09:00:00.000Z) levert. Hetzelfde gold voor deleted_at/updated_at bij soft-delete en voor de bewerk-/verwijderpaden van API-keys, organisaties, workspaces, leden, reacties en webhooks, evenals voor last_used_at van API-keys en last_triggered_at van webhooks. Alle schrijfpaden stempelen nu ISO 8601 (UTC, T en Z).
  • Impact: Clients die updated_at parseren met new 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') schrijft YYYY-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/codes en GET /v1/dpp pagineerden uitsluitend op basis van created_at. Codes uit POST /v1/codes/batch, POST /v1/dpp/batch en de CSV/XLSX-import delen echter één tijdstempel — zodra een batch groter was dan limit (standaard 20), leverde de tweede pagina de resterende rijen met dezelfde tijdstempel niet meer. De codes bestonden en waren via GET /v1/codes/:id bereikbaar, maar doken in de lijst (Dashboard, CLI qr3 list, SDKs, MCP) nooit op. De cursor is nu een keyset over (created_at, id).
  • API-wijziging: meta.pagination.next_cursor is 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 een 400 op 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_at op 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 direct 401.
  • Achtergrond: expires_at wordt 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-keys blijft 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: De ip_hash is 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-Type application/problem+json.
  • 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 daarnaast public_url. Dit veld is alleen ingevuld bij bestanden met visibility: 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.
  • 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/codes accepteren een links-array (0–20 items, http(s), ≤ 2048 tekens). Elke URL wordt gecontroleerd met Google Web Risk; een onveilige URL retourneert 422. 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 zijn noindex-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/codes stuurt 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.ts voorkomt regressies op confirm() 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. wu3qaa opent https://qr3.app/{shortCode} in een nieuw tabblad.
  • i18n: Tooltip-teksten voor Duits en Engels toegevoegd.
  • Tests: packages/dashboard/tests/dashboard.test.ts beschermt link-href, nieuw tabbladgedrag, noopener noreferrer en 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 nu qr3.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/pricing niet worden geblokkeerd door de redirect-Worker.
  • Tests: packages/redirect/tests/unit/redirect.test.ts controleert 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 alle dpp_scans van de API-key-workspace (active_dpps, scans_by_day, top_dpps met 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 (migratie 0011) — gescheiden van scans (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/:dppId met 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 bevat eu_compliance + preview.changed_fields.
  • Dashboard: Simulatorkaart in het DPP-detail (/dashboard/dpp/:dppId) — chips voor DE/AT/FR/IT/ES/NL + custom, status-dropdown, Preview EU impact / Save changes / Reset. Non-blocking via Remix useFetcher.
  • 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/validate levert daarnaast eu_compliance — dezelfde validator als GET /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-compliance met compliant / 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 textile met verplichte AGEC-keten (weven/breien → verven/bedrukken → confectie), per vezel origin_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/import accepteert CSV en XLSX (Worker-compatibel via SheetJS xlsx, ~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|xlsx levert kant-en-klare sjablonen voor batterij en textiel.
  • Dashboard: Drag-and-drop-upload onder /dashboard/dpp/import met template-proxy en inline CSV-download.

Non-breaking — LTS-uitbreidingen

Alle bovengenoemde wijzigingen zijn additief:

  • Bestaande POST /v1/dpp/validate-clients negeren het nieuwe eu_compliance-veld zonder wijzigingen.
  • Bestaande battery-flows zijn ongewijzigd.
  • market_countries is optioneel en heeft als standaardwaarde [].

Zie API-versiebeheer voor het breaking-change-beleid.