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.pdfenqr.epstekenenfgenbg, als parameter en als opgeslagen kleuren. Zwart en elke grijstint worden als grijswaarde geschreven (alleen zwartplaat), elke andere kleur als CMYK in hele percentages, bijvoorbeeld1F4E79als 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=transparenttekent er geen. Titel blijft zwart, logoplaat wit. - Geen wijziging zonder kleuren: Zonder
fg/bgen 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. NaDELETE /v1/accountleidt 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 eenmaal200en eenmaal404op. De webhookqr.deletedwordt precies één keer verzonden, voorheen twee keer. - Nieuw:
POSTenDELETE /v1/codes/:id/logoantwoorden met409(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 gelijktijdigeDELETE /v1/codes/:id/logo-aanroepen leveren beide200op. Details: Logo.
2026-09 · TypeScript-SDK 1.2.0
- Nieuw in
@qr3/sdk1.2.0:client.codes.update()accepteertappearanceen levert de resultaten van de contrastcontrole alsissuesin het resultaat; zonder bevindingen ontbreekt het veld.client.codes.imageUrl()accepteertfg,bgenecc. Elke code die de API retourneert (get,list,create,update,batchCreate) bevattitleenappearance. - 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
PATCHopexpires_atschoont 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 nog410op, en een op ‘nu’ gezette vervaltijd liet de redirect tot 24 uur lang doorlopen. - Opgelost: Een met
expires_ataangemaakte 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
POSTenDELETE /v1/codes/:id/logomet404en wijzigen ze de verwijderde code niet, net zoalsPATCHal 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/:idaccepteertappearancemetforeground_colorenbackground_color(#RRGGBB, de achtergrond ooktransparent).qr.svgenqr.pngtekenen de opgeslagen kleuren zonder parameters; een parameter zoals?fg=000000heeft 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 inmeta.issuesalswarningofcriticalgemeld; een transparante achtergrond is altijdcritical, nooit geblokkeerd. - Samenvoegen: Weggelaten velden behouden hun opgeslagen waarde,
nullzet deze terug. Gelijktijdige wijzigingen aan dezelfde code overschrijven elkaar niet meer. - In elk code-antwoord:
appearancestaat in elk code-antwoord en in de webhooksqr.createdenqr.updated.POST, de batch en de import weigeren dit met422. - 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 oftransparent) enecc(foutcorrectieL,M,Q,H). SVG en PNG tekenen de kleuren; PDF en EPS accepteren ze, maar tekenen ze pas in een latere release.eccis effectief in alle vier de formaten, een logo dwingt nog steedsHaf. - Transparant:
bg=transparentlevert 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=300in 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.pdfenqr.epssluiten een ingesteld logo nu net zo in alsqr.svgenqr.png(zie hieronder) — als ingebedde afbeelding (PDF via eenImage XObject, EPS via een afbeeldingsdictionary in PostScript, daar alleen met%%LanguageLevel: 3), gecentreerd op hetzelfde vlak, met dezelfde verhoging van de foutcorrectie naarH. Beide formaten leverden het logo voorheen niet mee; dit is nu opgelost. - Caching: Met een ingesteld logo leveren nu ook
qr.pdfenqr.epsCache-Control: public, max-age=300in 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.epsnog 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
viewerziet de status en het voorbeeld, maar geen van de drie acties. - API:
POST /v1/codes/{id}/logo(multipart-veldfile; PNG, JPEG of WebP, maximaal 1 MB, SVG wordt geweigerd) maakt een logo aan of vervangt dit;DELETE /v1/codes/{id}/logoverwijdert het weer en is idempotent. Een te grote afbeelding levert413op, een niet-ondersteund formaat422, de rolviewer403. - Weergave:
qr.svgenqr.pngsluiten het logo in als daadwerkelijke pixels (512 × 512, genormaliseerd) en verhogen daarvoor de foutcorrectie naarH. Toevoegen en verwijderen wijzigen hiermee het puntenpatroon (M↔H), vervangen niet.qr.pdfenqr.epssluiten 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.svgandqr.pngCache-Control: public, max-age=300in 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,archiveWorkspaceenzovoort. 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 —archiveWorkspaceis eenDELETE,importDppsis eenPOST. - 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.txtheeft 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:initializetegenhttps://mcp.qr3.app/mcpantwoordt met een veldinstructions(voorheen: elf tools zonder enige toelichting). Er is ook opgenomen welke aanroepen een API-sleutel nodig hebben —initializeentools/listniet, elketools/callwel. - Correcties in
llms.txt: Vier vermeldingen zijn getoetst aan de productieomgeving en bleken niet te kloppen: (1) de Python-SDKqr3appis niet gepubliceerd op PyPI — depip install-regel is zonder vervanging verwijderd; (2)Accept: text/markdowngeldt 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}/statsenGET /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/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.