Hoppa till innehåll

Changelog

Changelog

Kurerade höjdpunkter från de senaste versionerna. För fullständiga API-versioner och policy för brytande ändringar, se API-versionering & LTS-policy.

Detaljerade ändringar för enskilda slutpunkter: OpenAPI-specifikation och interaktiv API-referens.


2026-08 · Keyset-cursor nu på alla list-slutpunkter

  • Fix: Cursor-fixen för GET /v1/codes och GET /v1/dpp (se nedan) har nu rullats ut till alla övriga cursor-paginerade listor: GET /v1/qr-codes/:id/comments, /v1/workspaces, /v1/gs1/identifiers, /v1/members, /v1/audit-logs, /v1/admin/orgs, /v1/admin/users och /v1/webhooks/:id/deliveries. Alla bläddrade tidigare enbart via created_at; rader med identisk tidsstämpel (audit-loggposter från en batch-operation, webhook-retries, medlemsimporter) kunde gå förlorade på nästa sida. Sorteringsnyckeln är nu överallt tupeln (created_at, id).
  • API-ändring: I dessa listor är meta.pagination.next_cursor från och med nu också ett opakt värde (base64url) istället för en ren tidsstämpel; oläsbara cursors returnerar 400, gamla tidsstämpel-cursors accepteras fortfarande under en övergångsperiod. Undantag GET /v1/webhooks/:id/deliveries: där förblir next_cursor ID:t för den senaste leveransen (okänt ID → första sidan). Klienter som returnerar next_cursor oförändrat — Dashboard, CLI, SDKs, MCP — behöver inte ändra någonting.
  • Konsekvens: Ingen migrering krävs. Om du har saknat poster när du bläddrat i någon av dessa listor (t.ex. audit-loggen eller leveransloggen i Dashboard): De var aldrig borta — listorna visar dem fullständigt från och med nu.

2026-08 · Tidsstämplar efter redigering och radering åter OpenAPI-kompatibla

  • Fix: Efter PATCH /v1/codes/{id} returnerades updated_at i SQLite-format utan tidszon (2026-08-17 09:00:00), trots att OpenAPI-specifikationen utlovar format: date-time och skapande (POST) returnerar ISO-tidsstämpeln (2026-08-17T09:00:00.000Z). Detsamma gällde för deleted_at/updated_at vid soft-delete samt för redigerings-/raderingssökvägarna för API-nycklar, organisationer, workspaces, medlemmar, kommentarer och webhooks, och dessutom för last_used_at för API-nycklar och last_triggered_at för webhooks. Alla skrivsökvägar stämplar nu ISO 8601 (UTC, T och Z).
  • Konsekvens: Klienter som parsar updated_at med new Date(...) (SDKs, CLI, Dashboard) läste formatet med mellanslag som lokal tid — CLI visade tiden för koder som redigerats en gång förskjuten med den lokala tidsförskjutningen (Wien: −2 h). Detta är åtgärdat. Dessutom normaliserar en datamigrering redan sparade värden i det gamla formatet till ISO, så att sortering och jämförelser i blandade data blir korrekta. Inga ändringar av fältnamn eller svarsstruktur.
  • Bakgrund: Samma felklass som de två korrigeringarna nedan (utgångstid för API-nycklar, re-scan-cutoff): SQLites datetime('now') skriver YYYY-MM-DD HH:MM:SS, alla andra skrivare ISO 8601. Ett källkodstest (guard test) förhindrar framtida förekomster.

2026-08 · Listpaginering tappar inte längre batchkoder

  • Fix: GET /v1/codes och GET /v1/dpp paginerade enbart via created_at. Koder från POST /v1/codes/batch, POST /v1/dpp/batch och CSV/XLSX-importen delar dock på en tidsstämpel — så fort en batch var större än limit (standardvärde 20), returnerade den andra sidan inte längre de återstående raderna för samma tidsstämpel. Koderna existerade och var tillgängliga via GET /v1/codes/:id, men dök aldrig upp i listan (Dashboard, CLI qr3 list, SDKs, MCP). Cursorn är nu ett keyset över (created_at, id).
  • API-ändring: meta.pagination.next_cursor är från och med nu ett opakt värde (base64url) istället för en ren tidsstämpel. De som returnerar cursorn oförändrad som ?cursor= — precis som Dashboard, CLI, alla SDKs och MCP-servern gör — behöver inte ändra någonting. Gamla tidsstämpel-cursors accepteras fortfarande under en övergångsperiod; oläsbara cursors returnerar nu 400 istället för att tyst leverera den första sidan.
  • Konsekvens: För de som såg färre koder i listan efter en batchimport än vad som skapades: Koderna var aldrig borta — listan visar dem fullständigt från och med nu. Ingen migrering krävs.

2026-08 · Säkerhets-re-scans körs återigen i 24-timmarstakt

  • Fix: Den periodiska re-scanningen av mål-URL:er och länkar på landningssidor (Google Web Risk) hoppade över koder vars senaste skanning inföll på samma kalenderdag som 24-timmars-cutoffen — beroende på klockslag fördröjdes re-scannen med upp till ytterligare en dag. Cutoffen beräknas nu i samma ISO-format som skanningstidsstämplarna är sparade i.
  • Konsekvens: En mål-URL som klassificeras som osäker efter den senaste skanningen leder återigen till att koden pausas automatiskt inom det dokumenterade 24-timmarsfönstret. Inga ändringar i API eller svarsformat.

2026-08 · API-nycklar löper ut vid utgångstidpunkten

  • Fix: En API-nyckel vars expires_at inföll samma dag fortsatte att accepteras fram till midnatt UTC. Utgångstiden jämförs nu som en tidsstämpel istället för som en sträng — en utgången nyckel returnerar omedelbart 401.
  • Bakgrund: expires_at sparas som en ISO-tidsstämpel (2026-08-14T09:00:00Z), medan jämförelsesidan returnerade formatet med mellanslag (2026-08-14 09:00:00). Den råa strängjämförelsen stämde därför endast så länge som själva datumet skilde sig åt.
  • Konsekvens: Ingen migrering krävs, svarsformatet för GET /v1/api-keys förblir oförändrat. Oläsbara utgångsvärden betraktas nu som utgångna istället för giltiga.

2026-08 · API-referens: Tenant-hantering dokumenterad

  • OpenAPI: Specifikationen — och därmed den interaktiva referensen — dokumenterar nu organisationer (inkl. GET /v1/organizations/usage), workspaces, medlemmar & roller och audit-loggar.
  • Billing: Abonnemangsöversikten (GET /v1/billing/plans) är offentlig; checkout (POST /v1/billing/checkout) och Stripe-kundportalen (GET /v1/billing/portal) är angivna som slutpunkter för organisationsadministratörer.
  • Skanningsexport: Skanningsstatistik (GET /v1/codes/{id}/scans) och rådataexporten (…/scans.csv, …/scans.xlsx) är fullständigt dokumenterade — inklusive GDPR-information: ip_hash är aldrig inkluderad i exporten.
  • Felbeteende: Även 400-svaret från request-valideringen är nu dokumenterat: Bodyn är det råa Zod-felet, inte ett RFC-7807-problemdokument — det levereras ändå med Content-Type application/problem+json.

2026-08 · Kopiera offentliga fillänkar

  • Dashboard: Offentliga filer på koddetaljsidan har nu en knapp som kopierar deras offentliga länk till urklipp – direkt användbar som måladress för en QR-kod om en skanning omedelbart ska öppna ett specifikt dokument istället för landningssidan med fillistan.
  • API: Fil-slutpunkterna (/v1/files) returnerar nu även public_url. Fältet är endast satt för filer med visibility: public – privata filer får ingen offentlig adress.
  • Beteende: Länken kräver ingen inloggning och öppnar filen direkt i webbläsaren. Att ersätta filen lämnar länken oförändrad, så en tryckt kod förblir giltig. Detaljer: Filer & datablad.

2026-07 · Teamroller: Redaktör utan borttagning & administratörsfakturering

  • Nytt: Medlemsrollen Redaktör (utan borttagning) — skapar och redigerar QR-koder, filer och Digital Product Passports, men kan inte ta bort något eller skapa API-nycklar. Alla destruktiva slutpunkter kontrollerar rollen på serversidan (403).
  • Fakturering: Abonnemangsuppgraderingar och Stripe-kundportalen (POST /v1/billing/checkout, GET /v1/billing/portal) är nu reserverade för organisationsadministratörer — alla andra roller ser en skrivskyddad abonnemangsöversikt.
  • Dashboard: Åtgärder som den egna rollen inte tillåter döljs: En Läsare ser till exempel inga knappar för att skapa, redigera eller ta bort; listor, nedladdningar och statistik förblir synliga. Detaljer: Team & roller.

2026-06 · Externa länkar på kodens landningssida

  • Landningssida: Landningssidan för en kod som är värd på qr3 kan nu lista externa, självhostade länkar ({ label, url }) – utöver oder istället för uppladdade filer, till exempel för datablad på den egna webbplatsen.
  • API: POST/PATCH /v1/codes accepterar en links-array (0–20 poster, http(s), ≤ 2048 tecken). Varje URL kontrolleras med Google Web Risk; en osäker URL returnerar 422. En tom array tar bort alla länkar.
  • Dashboard: Lägg till, sortera och ta bort länkar på koddetaljsidan.
  • Säkerhet: Renderade länkar förblir XSS-säkra (escaped, endast http(s)) och sidan behåller sin noindex-header.

2026-04 · Dashboard-analys per QR-kod

  • Dashboard: Analysknappen i QR-kodlistan öppnar nu statistik-sidan för respektive QR-kod under /dashboard/codes/{id}.
  • Routing: Aliaset /dashboard/codes omdirigerar fortfarande till /dashboard, men fångar inte längre upp detaljerade rutter som /dashboard/codes/{id}.
  • API: Detaljsidan läser in QR-koden direkt via GET /v1/codes/:id; därmed är den inte längre beroende av pagineringsgränser för listor.
  • Tester: Regressionstester täcker alias-omdirigeringen och den direkta inläsningen av koden.

2026-04 · Dashboard-borttagningsdialog för QR-koder

  • Dashboard: Papperskorgen i QR-kodlistan öppnar nu en egen React-dialogruta istället för en inbyggd popup i webbläsaren.
  • Feedback: Efter borttagning visas ett toast-meddelande för framgång eller fel.
  • Tester: packages/dashboard/tests/dashboard.test.ts förhindrar regressioner för confirm() i QR-kodens borttagningsflöde.

2026-04 · Dashboard-kortlänkstest för dynamiska QR-koder

  • Dashboard: Shortcodes in QR-kodlistan är nu direkt klickbara som externa omdirigeringslänkar. Ikonen för extern länk bredvid t.ex. wu3qaa öppnar https://qr3.app/{shortCode} i en ny flik.
  • i18n: Tooltip-texter för tyska och engelska har lagts till.
  • Tester: packages/dashboard/tests/dashboard.test.ts skyddar länk-href, beteende för ny flik, noopener noreferrer och ikon mot regressioner.

2026-04 · Redirect-Worker-rutt för dynamiska QR-koder

  • Fix: Dynamiska QR-koder under https://qr3.app/{shortCode} hanteras återigen av omdirigerings-workern. Produktionsrutten använder nu qr3.app/* eftersom Cloudflare Workers rutter inte stöder :code-sökvägsparametrar.
  • Härdning: Sökvägar som inte matchar skickas vidare till landnings-origin, så att vanliga sidor som /de/pricing inte blockeras av omdirigerings-workern.
  • Tester: packages/redirect/tests/unit/redirect.test.ts testar wildcard-rutten, shortcode-bearbetningen och origin-pass-through.

2026-04 · Workspace-DPP-skanningsöversikt (Q3.4.2)

  • Nytt: GET /v1/workspace/stats/dpp?days=30 — aggregerar alla dpp_scans för API-nyckelns workspace (active_dpps, scans_by_day, top_dpps med produktnamn/kategori).
  • Dashboard: Kort på startsidan (/dashboard) med 30-dagars stapeldiagram + topplistor — parallellt med QR-kodkorten.
  • Public: Marknadsföringskortlänk GET /dpp/dpp_<id> (ett segment) för livedemos, parallellt med /dpp/{gtin}/{serial}.

2026-04 · DPP-skanningsanalys (Q3.4.1)

  • Nytt: GET /v1/dpp/:id/stats?days=30 — aggregerade skanningar från den offentliga GS1-resolveraren per DPP. Fält: total_scans, period_scans, scans_by_day, top_countries, top_devices, top_representations.
  • Nytt: Tabellen dpp_scans (migrering 0011) — separerad från scans (omdirigerings-worker). IP-adresser hashas med ett dagligen roterande salt, råa IP-adresser når aldrig D1.
  • Dashboard: Mini-diagramkort (SVG, inget diagrambibliotek) på /dashboard/dpp/:dppId med 30-dagarsstaplar + topp 3-uppdelningar. Empty-state så snart en DPP är live men ännu inte har haft några skanningar.

2026-04 · Live-EU-efterlevnadssimulator (Q3.3.7)

  • Nytt: POST /v1/dpp/:id/validate-update — simulerar delvisa uppdateringar stateless (status, marknadslista, …) utan persistens. Svaret innehåller eu_compliance + preview.changed_fields.
  • Dashboard: Simulatorkort i DPP-detaljvyn (/dashboard/dpp/:dppId) — chips för DE/AT/FR/IT/ES/NL + anpassad, status-rullgardinsmeny, Preview EU impact / Save changes / Reset. Icke-blockerande via Remix useFetcher.
  • Härdning: Utbrutna simulatorhjälpare (readUpdatePatchFromForm, marketCountriesKey) + 18 nya enhetstester; buggfix: Enstaka icke-ISO-indata tömmer inte längre marknadslistan.

2026-04 · Live-EU-efterlevnadsförhandsvisning i skapa-formuläret (Q3.3.6)

  • Ändrat: POST /v1/dpp/validate returnerar nu även eu_compliance — samma validerare som GET /v1/dpp/:id/eu-compliance, stateless före sparning.
  • Dashboard: Förhandsvisning under den befintliga valideringspanelen + ny Save-Guard-banner före skicka-knapparna om det finns öppna fel/varningar (i18n-pluralisering DE/EN).

2026-04 · EU-validerare + textil-UI (Q3.3.4 + Q3.3.5)

  • Nytt: EU-efterlevnadsvaliderare med 5 textilregler (TEXTILE_AGEC_REQUIRED, TEXTILE_MICROPLASTICS_CONSISTENCY, TEXTILE_SVHC_THRESHOLD, TEXTILE_GREENWASHING, TEXTILE_ESPR_READY).
  • Nytt: GET /v1/dpp/:id/eu-compliance med compliant / espr_ready / issues[] / summary.
  • Dashboard: EU-efterlevnadssektion i DPP-detaljvyn (sammanfattningskort, grupperade problemkort, ESPR-Ready-badge i headern).

2026-04 · Textil-DPP-schema (Q3.3.1–Q3.3.3)

  • Nytt: Kategorin textile med obligatorisk AGEC-kedja (vävning/stickning → färgning/tryckning → konfektion), per fiber origin_country + recycled_pct, svhc_substances[], ESPR-opt-in (PEF, livslängd, återvinningsbarhet).
  • Nytt: Basfältet market_countries: string[] (ISO 3166-1 alpha-2) på alla DPP-kategorier — styr FR-specifika AGEC-regler och det franska obligatoriska konsumentmeddelandet.
  • Nytt: Konsument-HTML-mall med AGEC-mikroplastvarning, ursprungskedja i 3 steg (flaggpiller), SVHC-lista samt sektioner för hållbarhet och återvinningsbarhet.
  • Migrering: 0010_dpp_market_countries (D1).

2026-04 · DPP-massimport (Q3.2.1–Q3.2.5)

  • Nytt: POST /v1/dpp/import accepterar CSV och XLSX (Worker-kompatibel via SheetJS xlsx, ~283 KB gzip-bundle).
  • Skalat: abonnemangsbaserad gräns (Free 100 → Enterprise 10k) + uppdelad db.batch() à 100 + 5 MB body-gräns.
  • Nytt: Felrapport som CSV i fältet errors_csv i 201-svaret; GET /v1/dpp/import/templates/:category?format=csv|xlsx levererar färdiga mallar för batteri och textil.
  • Dashboard: Dra-och-släpp-uppladdning under /dashboard/dpp/import med mall-proxy och inline-CSV-nedladdning.

Icke-brytande — LTS-utökningar

Alla ovan nämnda ändringar är additiva:

  • Befintliga POST /v1/dpp/validate-klienter ignorerar det nya eu_compliance-fältet utan ändringar.
  • Befintliga battery-flöden är oförändrade.
  • market_countries är valfritt och har standardvärdet [].

Se API-versionering för policyn gällande brytande ändringar.