Changelog
Changelog
Kuratierte Highlights der letzten Releases. Für vollständige API-Versionen und Breaking-Change-Policy siehe die API-Versionierung & LTS-Policy.
Detaillierte Änderungen an einzelnen Endpoints: OpenAPI-Spezifikation und interaktive API-Referenz.
2026-10 · Farben im Dashboard
- Neu: Die Detailseite eines Codes hat die Karte Farben: Vorder- und
Hintergrund als Hex-Wert, transparenter Hintergrund, Vorschau über die
echte Bildroute. Geprüft wird wie bei
PATCH /v1/codes/{id}: Kritische Paare brauchen eine Bestätigung, Paare unter 1,5 : 1 lassen sich nicht speichern. - Zurücksetzen auf Schwarz/Weiß liefert in allen vier Formaten wieder dieselben Bytes wie ohne Farben. Codes ohne Farben ändern sich nicht.
- Transparente Codes erscheinen in allen Dashboard-Vorschauen auf einem Schachbrett. Details: Farben im Dashboard.
2026-10 · Farben in PDF und EPS
- Neu:
qr.pdfundqr.epszeichnenfgundbg, als Parameter und als gespeicherte Farben. Schwarz und jedes Grau werden als Graustufe geschrieben (nur Schwarzplatte), jede andere Farbe als CMYK in ganzen Prozent, etwa1F4E79als C74 M36 Y0 K53. - Hintergrund wie beim SVG: Sobald eine Farbe gewählt ist, liegt eine
deckende Fläche hinter dem Code und seiner Ruhezone, weiß oder
bg.bg=transparentzeichnet keine. Titel bleibt schwarz, Logo-Platte weiß. - Keine Änderung ohne Farben: Ohne
fg/bgund ohne gespeicherte Farben liefern beide Formate dieselben Bytes wie vorher. Das PDF eines Produktpasses bleibt ungefärbt. - Grenzen: kein ICC-Profil, kein PDF/X. Details: Farben im Druck.
2026-09 · Statische Codes ohne Weiterleitungs-Cache, gleichzeitiges Löschen
- Geändert: Statische Codes liegen nicht mehr im Weiterleitungs-Cache. Ein
Aufruf ihres Kurz-Links (
redirect_url) liest immer den aktuellen Stand. NachDELETE /v1/accountleitet ein statischer Code deshalb beim nächsten Aufruf nicht mehr weiter, vorher bis zu 24 Stunden lang. Auch ein Tarifwechsel wirkt bei statischen Codes sofort. Dynamische Codes räumt die Kontolöschung wie bisher. Gedruckte statische Codes enthalten ihr Ziel direkt und sind nicht betroffen. - Behoben: Zwei gleichzeitige
DELETE /v1/codes/:idauf denselben Code liefern einmal200und einmal404. Der Webhookqr.deletedwird genau einmal gesendet, vorher zweimal. - Neu:
POSTundDELETE /v1/codes/:id/logoantworten mit409(errors/conflict), wenn eine andere Anfrage das Logo desselben Codes gleichzeitig geändert hat, und ändern dann nichts. Vorher konnte ein hochgeladenes Logo ungenutzt gespeichert bleiben. Zwei gleichzeitigeDELETE /v1/codes/:id/logoliefern beide200. Details: Logo.
2026-09 · TypeScript-SDK 1.2.0
- Neu in
@qr3/sdk1.2.0:client.codes.update()nimmtappearancean und liefert die Befunde der Kontrastprüfung alsissuesam Ergebnis; ohne Befund fehlt das Feld.client.codes.imageUrl()nimmtfg,bgundeccan. Jeder Code, den die API zurückgibt (get,list,create,update,batchCreate), trägttitleundappearance. - Aktualisieren:
npm install @qr3/sdk@latest. Python, Go und PHP folgen. Details: SDKs & CLI und Farben am Code speichern.
2026-09 · Geänderte Ablaufzeit gilt innerhalb von etwa einer Minute
- Behoben: Ein
PATCHaufexpires_aträumt jetzt den Weiterleitungs-Cache des Codes. Die neue Ablaufzeit gilt damit innerhalb von etwa einer Minute statt erst bis zu 24 Stunden später. Vorher lieferte ein entfernter oder verschobener Ablauf bis zu 24 Stunden weiter410, und ein auf jetzt gesetzter Ablauf ließ die Weiterleitung bis zu 24 Stunden weiterlaufen. - Behoben: Ein mit
expires_atangelegter Code läuft auch dann pünktlich ab, wenn der Ablauf innerhalb der ersten 24 Stunden liegt. - Behoben: Wird ein Code gelöscht, während sein Logo hochgeladen oder
entfernt wird, antworten
POSTundDELETE /v1/codes/:id/logomit404und ändern den gelöschten Code nicht, wie schonPATCH. - Behoben: Auch ein statischer Code, dessen Kurz-Link (
redirect_url) aufgerufen wurde, liegt im Weiterleitungs-Cache. Ändern, Pausieren und Löschen räumen ihn jetzt ebenfalls, nicht nur bei dynamischen Codes.
2026-09 · Farben am Code speichern
- Neu:
PATCH /v1/codes/:idnimmtappearancemitforeground_colorundbackground_coloran (#RRGGBB, der Hintergrund auchtransparent).qr.svgundqr.pngzeichnen die gespeicherten Farben ohne Parameter; ein Parameter wie?fg=000000hat weiter Vorrang. PDF und EPS zeichnen Farben erst mit einem späteren Release. - Kontrastprüfung: Ein Paar unter 1,5:1 ergibt
422. Schwächere Paare werden gespeichert und inmeta.issuesalswarningodercriticalgemeldet; ein transparenter Hintergrund ist immercritical, nie gesperrt. - Zusammenführen: Weggelassene Felder behalten ihren gespeicherten Wert,
nullsetzt zurück. Gleichzeitige Änderungen am selben Code überschreiben sich nicht mehr gegenseitig. - In jeder Code-Antwort:
appearancesteht in jeder Code-Antwort und in den Webhooksqr.createdundqr.updated.POST, der Batch und der Import lehnen es mit422ab. - Keine Änderung für bestehende Codes: Ohne gespeicherte Farben liefert jeder Code dieselben Bytes wie vorher. Details: Farben am Code speichern.
2026-09 · Farben und Fehlerkorrektur als Parameter der Bildrouten
- Neu: Alle vier Bildrouten nehmen
fg(Vordergrundfarbe),bg(Hintergrundfarbe odertransparent) undecc(FehlerkorrekturL,M,Q,H) an. SVG und PNG zeichnen die Farben; PDF und EPS nehmen sie an, zeichnen sie aber erst mit einem späteren Release.eccwirkt in allen vier Formaten, ein Logo erzwingt weiterH. - Transparenz:
bg=transparentliefert ein SVG ohne Hintergrund und ein PNG mit echtem Alphakanal. Der Untergrund muss hell sein und die Ruhezone frei lassen. - Nie ein Fehler: Ungültige Werte werden ignoriert; das Bild kommt dann genau wie ohne Parameter.
- Zwischenspeicherung: Jeder wirksame Parameter liefert
Cache-Control: public, max-age=300statt der 24 Stunden des Standardbilds. - Keine Verhaltensänderung ohne Parameter: Jeder bestehende Code liefert in allen vier Formaten dieselben Bytes wie vorher.
- In jedem Tarif verfügbar. Details: Farben und Fehlerkorrektur.
2026-09 · PDF und EPS betten das Logo jetzt ebenfalls ein
- Erweiterung:
qr.pdfundqr.epsbetten ein gesetztes Logo jetzt genauso ein wieqr.svgundqr.png(siehe unten) — als eingebettetes Bild (PDF über einImage XObject, EPS über ein Bilddictionary in PostScript, dort nur mit%%LanguageLevel: 3), zentriert auf derselben Fläche, mit derselben Anhebung der Fehlerkorrektur aufH. Beide Formate lieferten das Logo bisher nicht mit aus; das ist jetzt behoben. - Zwischenspeicherung: Mit gesetztem Logo liefern jetzt auch
qr.pdfundqr.epsCache-Control: public, max-age=300statt der sonst üblichen 24 Stunden — genau wie SVG/PNG. - Rückfall: Kann das Logo-Bild selbst nicht aufbereitet werden (etwa ein
beschädigtes gespeichertes Objekt), bleibt die Fehlerkorrektur
H, aber die reservierte Fläche bleibt leer statt eines Bilds — nie ein Serverfehler. - Keine Verhaltensänderung ohne Logo: Codes ohne Logo liefern
qr.pdf/qr.epsweiterhin byte-identisch, mit unverändertem 24-Stunden-Cache. - Details und Druckhinweise: Logo im QR-Code.
2026-09 · Logo im QR-Code — Dashboard und API
- Neu: Ein Code kann jetzt ein Logo in der Mitte tragen. Im Dashboard
legt die Detailseite eines Codes eine eigene Logo-Karte an: Logo
auswählen, beim ersten Hinzufügen oder beim Entfernen eine Warnung mit
Pflicht-Häkchen bestätigen (beides ändert das Punktmuster), dann Logo
hochladen/Logo entfernen. Ein Ersetzen braucht keine Bestätigung —
nur das Bild wechselt, nicht das Muster. Die Rolle
viewersieht Status und Vorschau, aber keine der drei Aktionen. - API:
POST /v1/codes/{id}/logo(Multipart-Feldfile; PNG, JPEG oder WebP, höchstens 1 MB, SVG wird abgelehnt) legt ein Logo an oder ersetzt es;DELETE /v1/codes/{id}/logoentfernt es wieder und ist idempotent. Ein zu großes Bild liefert413, ein nicht unterstütztes Format422, die Rolleviewer403. - Darstellung:
qr.svgundqr.pngbetten das Logo als tatsächliche Pixel ein (512 × 512, normalisiert) und heben dafür die Fehlerkorrektur aufHan. Hinzufügen und Entfernen ändern damit das Punktmuster (M↔H), ein Ersetzen nicht.qr.pdfundqr.epsbetten kein Logo ein und bleiben unverändert. Ein bereits gedruckter Code funktioniert in jedem Fall weiter, weil das kodierte Ziel nicht am Logo hängt — neu drucken muss nur, wer die (neue) Logo-Optik auch auf dem Material sehen will, und dabei alte und neue Druckdateien nicht mischen. - Zwischenspeicherung: Mit gesetztem Logo liefern
qr.svgundqr.pngCache-Control: public, max-age=300statt der sonst üblichen 24 Stunden. Details, Grenzwerte und Druckhinweise: Logo im QR-Code.
2026-08 · operationId für alle 75 Operationen, und eine Wann-Anleitung für Agenten
- Ergänzung: Alle 75 Operationen der OpenAPI-Spezifikation tragen jetzt eine
operationId—listCodes,createCode,getCodeStats,archiveWorkspaceund so fort. Bisher fehlte das Feld durchgehend, sodass jeder Generator den Methodennamen aus HTTP-Methode und Pfad ableiten musste (postV1Codes). Solche Namen hängen am Pfad und ändern sich mit jedem Pfad-Umbau. Namensform an allen 75 Stellen gleich:list/get/create/update/replace/delete, sonst das Verb der Fachhandlung (validateDpp,registerGs1Identifier,pingWebhook). Das Verb folgt der Fachlichkeit, nicht der HTTP-Methode —archiveWorkspaceist einDELETE,importDppseinPOST. - Auswirkung auf selbst generierte Clients: Wer sein SDK aus der Spezifikation generiert, bekommt beim nächsten Lauf umbenannte Methoden (
postV1Codes→createCode). Das ist der Zweck der Änderung, aber es ist ein Umbenennen in fremdem Code — deshalb hier ausdrücklich genannt. Pfade, Parameter, Antwortformen und Statuscodes sind unverändert; die offiziellen SDKs, CLI und MCP-Server sind nicht betroffen. - Agenten-Einstieg:
https://qr3.app/llms.txthat einen Abschnitt## When to use qr3.app— sechs Aufgaben statt sechs Funktionen, plus den Satz, wofür qr3.app nicht das richtige Werkzeug ist. Dieselbe Auskunft liefert der MCP-Server jetzt im Handschlag:initializegegenhttps://mcp.qr3.app/mcpantwortet mit einem Feldinstructions(vorher: elf Werkzeuge ohne jede Einordnung). Enthalten ist auch, welche Aufrufe einen API-Schlüssel brauchen —initializeundtools/listnicht, jedestools/callschon. - Korrekturen in
llms.txt: Vier Angaben wurden gegen Produktion gemessen und hielten nicht: (1) das Python-SDKqr3appist auf PyPI nicht veröffentlicht — diepip install-Zeile ist ersatzlos raus; (2)Accept: text/markdowngilt für Startseite und Blog, nicht für jede serverseitig gerenderte Seite; (3) die Kontaktadresse lautet[email protected], auch im JSON-LD der Startseite; (4)/de/security/ist eine Weiterleitung auf einen Anker, keine eigene Seite. Neu verlinkt:docs.qr3.app/de/skills/. - Absicherung: Drei Zusicherungen in
openapi-spec.test.ts— Vollzähligkeit, Eindeutigkeit,lowerCamelCase—, jede mit einer Mutation gegengeprüft. Neun weitere Tests halten die vier korrigierten Angaben fest, damit sie nicht zurückkehren. - Bekannte Einschränkung: Zwei der 75 Operationen sind in der Spezifikation beschrieben, antworten aber
404(GET /v1/codes/{id}/statsundGET /v1/account). Sie haben hier einen Namen bekommen und verlieren ihn wieder, sobald entschieden ist, ob sie gebaut oder gestrichen werden.
2026-08 · Tarif-Downgrades und Kündigungen wirken jetzt auch auf die Limits
- Fix: Der Stripe-Webhook schrieb bei Planwechsel und Kündigung nur den Plannamen, nicht die vier Limit-Spalten der Organisation (
max_workspaces,max_members,max_dynamic_codes,max_scans_per_month). Da die Durchsetzung das Maximum aus gespeichertem Wert und Plan-Baseline nimmt, behielt eine herabgestufte oder gekündigte Organisation ihre alten, höheren Limits — das Downgrade war wirkungslos. Der Webhook setzt die Spalten jetzt bei jedem echten Planwechsel auf die Baseline des neuen Tarifs und bei Kündigung auf die Free-Baseline — genau wie es die Admin-Verwaltung bereits tut. - Verhalten: Routine-Updates der Subscription (Verlängerung, Zahlungsmittel, Proration) fassen die Limits weiterhin nicht an; individuell gewährte höhere Limits überstehen sie unverändert. Erst ein echter Planwechsel setzt sie auf die Baseline des neuen Tarifs.
- Auswirkung: Keine API- oder Antwortformat-Änderung. Organisationen, deren Downgrade vor diesem Fix lag, behalten die alten Werte, bis der nächste Planwechsel greift oder der Support sie anpasst.
2026-08 · Keyset-Cursor jetzt auf allen Listen-Endpoints
- Fix: Der Cursor-Fix für
GET /v1/codesundGET /v1/dpp(siehe unten) ist jetzt auf alle übrigen cursor-paginierten Listen ausgerollt:GET /v1/qr-codes/:id/comments,/v1/workspaces,/v1/gs1/identifiers,/v1/members,/v1/audit-logs,/v1/admin/orgs,/v1/admin/usersund/v1/webhooks/:id/deliveries. Alle blätterten allein übercreated_at; Zeilen mit identischem Zeitstempel (Audit-Einträge einer Batch-Operation, Webhook-Retries, Mitglieder-Importe) konnten auf der Folgeseite verloren gehen. Sortierschlüssel ist jetzt überall das Tupel(created_at, id). - API-Änderung: Auf diesen Listen ist
meta.pagination.next_cursorab sofort ebenfalls ein opaker Wert (base64url) statt eines nackten Zeitstempels; unlesbare Cursor liefern400, alte Zeitstempel-Cursor werden übergangsweise weiter akzeptiert. AusnahmeGET /v1/webhooks/:id/deliveries: dort bleibtnext_cursordie ID der letzten Zustellung (unbekannte ID → erste Seite). Clients, dienext_cursorunverändert zurückgeben — Dashboard, CLI, SDKs, MCP —, müssen nichts ändern. - Auswirkung: Keine Migration nötig. Wer in einer dieser Listen (z. B. Audit-Log oder Zustellungs-Log im Dashboard) beim Blättern Einträge vermisst hat: Sie waren nie weg — die Listen zeigen sie ab jetzt vollständig.
2026-08 · Zeitstempel nach Bearbeiten und Löschen wieder OpenAPI-konform
- Fix: Nach
PATCH /v1/codes/{id}kamupdated_atim SQLite-Format ohne Zeitzone zurück (2026-08-17 09:00:00), obwohl die OpenAPI-Spezifikationformat: date-timezusagt und Anlegen (POST) den ISO-Zeitstempel (2026-08-17T09:00:00.000Z) liefert. Dasselbe galt fürdeleted_at/updated_atbeim Soft-Delete sowie für die Bearbeiten-/Löschen-Pfade von API-Keys, Organisationen, Workspaces, Mitgliedern, Kommentaren und Webhooks, außerdem fürlast_used_atvon API-Keys undlast_triggered_atvon Webhooks. Alle Schreibpfade stempeln jetzt ISO 8601 (UTC,TundZ). - Auswirkung: Clients, die
updated_atmitnew Date(...)parsen (SDKs, CLI, Dashboard), lasen das Leerzeichen-Format als Ortszeit — die CLI zeigte bei einmal bearbeiteten Codes die Uhrzeit um den lokalen Offset verschoben (Wien: −2 h). Das ist behoben. Zusätzlich normalisiert eine Daten-Migration bereits gespeicherte Werte im alten Format auf ISO, damit Sortierung und Vergleiche in gemischten Beständen stimmen. Keine Änderung an Feldnamen oder Antwortstruktur. - Hintergrund: Gleiche Fehlerklasse wie die beiden Fixes unten (API-Key-Ablauf, Re-Scan-Cutoff): SQLites
datetime('now')schreibtYYYY-MM-DD HH:MM:SS, alle übrigen Schreiber ISO 8601. Ein Quelltext-Guard-Test verhindert künftig neue Vorkommen.
2026-08 · Listen-Pagination verliert keine Batch-Codes mehr
- Fix:
GET /v1/codesundGET /v1/dpppaginierten allein übercreated_at. Codes ausPOST /v1/codes/batch,POST /v1/dpp/batchund dem CSV/XLSX-Import teilen sich aber einen Zeitstempel — sobald ein Batch größer war alslimit(Default 20), lieferte die zweite Seite die restlichen Zeilen desselben Zeitstempels nicht mehr. Die Codes existierten und waren perGET /v1/codes/:iderreichbar, tauchten in der Liste (Dashboard, CLIqr3 list, SDKs, MCP) aber nie auf. Der Cursor ist jetzt ein Keyset über(created_at, id). - API-Änderung:
meta.pagination.next_cursorist ab sofort ein opaker Wert (base64url) statt eines nackten Zeitstempels. Wer den Cursor unverändert als?cursor=zurückgibt — so wie Dashboard, CLI, alle SDKs und der MCP-Server es tun — muss nichts ändern. Alte Zeitstempel-Cursor werden übergangsweise weiter akzeptiert; unlesbare Cursor liefern jetzt400statt stillschweigend die erste Seite. - Auswirkung: Wer nach einem Batch-Import in der Liste weniger Codes gesehen hat, als angelegt wurden: Die Codes waren nie weg — die Liste zeigt sie ab jetzt vollständig. Keine Migration nötig.
2026-08 · Sicherheits-Re-Scans laufen wieder im 24-Stunden-Takt
- Fix: Das periodische Re-Scanning von Ziel-URLs und Landingpage-Links (Google Web Risk) übersprang Codes, deren letzter Scan am selben Kalendertag wie der 24-Stunden-Cutoff lag — je nach Uhrzeit verzögerte sich der Re-Scan um bis zu einen weiteren Tag. Der Cutoff wird jetzt im selben ISO-Format berechnet, in dem die Scan-Zeitstempel gespeichert sind.
- Auswirkung: Eine Ziel-URL, die nach dem letzten Scan als unsicher eingestuft wird, führt wieder innerhalb des dokumentierten 24-Stunden-Fensters zum automatischen Pausieren des Codes. Keine API- oder Antwortformat-Änderung.
2026-08 · API-Keys laufen zum Ablaufzeitpunkt ab
- Fix: Ein API-Key, dessen
expires_atam selben Tag lag, wurde bis Mitternacht UTC weiter akzeptiert. Der Ablauf wird jetzt als Zeitstempel verglichen statt als Zeichenkette — ein abgelaufener Key liefert sofort401. - Hintergrund:
expires_atwird als ISO-Zeitstempel gespeichert (2026-08-14T09:00:00Z), die Vergleichsseite lieferte das Leerzeichen-Format (2026-08-14 09:00:00). Der rohe Zeichenketten-Vergleich stimmte deshalb nur, solange sich schon das Datum unterschied. - Auswirkung: Keine Migration nötig, das Antwortformat von
GET /v1/api-keysbleibt unverändert. Unlesbare Ablaufwerte gelten jetzt als abgelaufen statt als gültig.
2026-08 · API-Referenz: Tenant-Verwaltung dokumentiert
- OpenAPI: Die Spezifikation — und damit die interaktive Referenz — dokumentiert jetzt Organisationen (inkl.
GET /v1/organizations/usage), Workspaces, Mitglieder & Rollen und Audit-Logs. - Billing: Die Tarifübersicht (
GET /v1/billing/plans) ist öffentlich; Checkout (POST /v1/billing/checkout) und Stripe-Kundenportal (GET /v1/billing/portal) sind als Endpoints für Organisations-Administratoren ausgewiesen. - Scan-Export: Scan-Statistiken (
GET /v1/codes/{id}/scans) und der Rohdaten-Export (…/scans.csv,…/scans.xlsx) sind vollständig dokumentiert — inklusive DSGVO-Hinweis: Derip_hashist nie im Export enthalten. - Fehlerverhalten: Neu dokumentiert ist auch die
400-Antwort der Request-Validierung: Der Body ist der rohe Zod-Fehler, kein RFC-7807-Problem-Dokument — ausgeliefert wird er dennoch unter dem Content-Typeapplication/problem+json.
2026-08 · Öffentliche Datei-Links kopieren
- Dashboard: Öffentliche Dateien auf der Code-Detailseite haben jetzt einen Button, der ihren öffentlichen Link in die Zwischenablage legt – direkt als Ziel-URL eines QR-Codes verwendbar, wenn ein Scan sofort ein bestimmtes Dokument öffnen soll statt der Landingpage mit der Dateiliste.
- API: Die Datei-Endpoints (
/v1/files) liefern zusätzlichpublic_url. Das Feld ist nur bei Dateien mitvisibility: publicgesetzt – private Dateien bekommen keine öffentliche Adresse. - Verhalten: Der Link braucht keine Anmeldung und öffnet die Datei direkt im Browser. Ein Ersetzen der Datei lässt ihn unverändert, ein darauf gedruckter Code bleibt also gültig. Details: Dateien & Datenblätter.
2026-07 · Team-Rollen: Bearbeiter ohne Löschen & Admin-Abrechnung
- Neu: Mitgliederrolle Bearbeiter (ohne Löschen) — legt QR-Codes, Dateien und Digital Product Passports an und bearbeitet sie, kann aber nichts löschen und keine API-Keys anlegen. Alle destruktiven Endpoints prüfen die Rolle serverseitig (
403). - Abrechnung: Tarif-Upgrades und das Stripe-Kundenportal (
POST /v1/billing/checkout,GET /v1/billing/portal) sind jetzt Organisations-Administratoren vorbehalten — alle anderen Rollen sehen eine schreibgeschützte Tarifübersicht. - Dashboard: Aktionen, die die eigene Rolle nicht zulässt, werden ausgeblendet: Ein Betrachter sieht z. B. keine Buttons zum Anlegen, Bearbeiten oder Löschen; Listen, Downloads und Statistiken bleiben sichtbar. Details: Team & Rollen.
2026-06 · Externe Links auf der Code-Landingpage
- Landingpage: Die von qr3 gehostete Landingpage eines Codes kann jetzt externe, selbst gehostete Links (
{ label, url }) auflisten – zusätzlich zu oder anstelle von hochgeladenen Dateien, etwa für Datenblätter auf der eigenen Seite. - API:
POST/PATCH /v1/codesakzeptieren einlinks-Array (0–20 Einträge,http(s), ≤ 2048 Zeichen). Jede URL wird mit Google Web Risk geprüft; eine unsichere URL liefert422. Ein leeres Array löscht alle Links. - Dashboard: Links auf der Code-Detailseite hinzufügen, sortieren und entfernen.
- Sicherheit: Gerenderte Links bleiben XSS-sicher (escaped, nur
http(s)) und die Seite behält ihrennoindex-Header.
2026-04 · Dashboard-Analytics pro QR-Code
- Dashboard: Der Analytics-Button in der QR-Code-Liste öffnet jetzt die Statistikseite des jeweiligen QR-Codes unter
/dashboard/codes/{id}. - Routing: Der Alias
/dashboard/codesleitet weiterhin auf/dashboardum, fängt aber keine Detailrouten wie/dashboard/codes/{id}mehr ab. - API: Die Detailseite lädt den QR-Code direkt per
GET /v1/codes/:id; dadurch ist sie nicht mehr von Listen-Pagination-Limits abhängig. - Tests: Regressionstests decken den Alias-Redirect und den direkten Code-Load ab.
2026-04 · Dashboard-Löschdialog für QR-Codes
- Dashboard: Der Mistkübel in der QR-Code-Liste öffnet jetzt einen eigenen React-Dialog statt eines nativen Browser-Popups.
- Feedback: Nach dem Löschen erscheint eine Toast-Rückmeldung für Erfolg oder Fehler.
- Tests:
packages/dashboard/tests/dashboard.test.tsverhindert Regressionen aufconfirm()im QR-Code-Löschflow.
2026-04 · Dashboard-Kurzlink-Test für dynamische QR-Codes
- Dashboard: Shortcodes in der QR-Code-Liste sind jetzt direkt als externe Redirect-Links klickbar. Das External-Link-Icon neben z. B.
wu3qaaöffnethttps://qr3.app/{shortCode}in einem neuen Tab. - i18n: Tooltip-Texte für Deutsch und Englisch ergänzt.
- Tests:
packages/dashboard/tests/dashboard.test.tsschützt Link-Href, neues Tab-Verhalten,noopener noreferrerund Icon gegen Regressionen.
2026-04 · Redirect-Worker-Route für dynamische QR-Codes
- Fix: Dynamische QR-Codes unter
https://qr3.app/{shortCode}werden wieder vom Redirect-Worker verarbeitet. Die Production-Route nutzt jetztqr3.app/*, weil Cloudflare Worker-Routen keine:code-Pfadparameter unterstützen. - Härtung: Nicht passende Pfade werden an die Landing-Origin durchgereicht, damit normale Seiten wie
/de/pricingnicht vom Redirect-Worker blockiert werden. - Tests:
packages/redirect/tests/unit/redirect.test.tsprüft die Wildcard-Route, Shortcode-Verarbeitung und Origin-Pass-through.
2026-04 · Workspace-DPP-Scan-Übersicht (Q3.4.2)
- Neu:
GET /v1/workspace/stats/dpp?days=30— aggregiert alledpp_scansdes API-Key-Workspaces (active_dpps,scans_by_day,top_dppsmit Produktname/Kategorie). - Dashboard: Karte auf der Startseite (
/dashboard) mit 30-Tage-Balkendiagramm + Top-Listen — parallel zu den QR-Code-Karten. - Public: Marketing-Shortlink
GET /dpp/dpp_<id>(ein Segment) für Live-Demos, parallel zu/dpp/{gtin}/{serial}.
2026-04 · DPP-Scan-Analytics (Q3.4.1)
- Neu:
GET /v1/dpp/:id/stats?days=30— aggregierte Scans des öffentlichen GS1-Resolvers pro DPP. Felder:total_scans,period_scans,scans_by_day,top_countries,top_devices,top_representations. - Neu: Tabelle
dpp_scans(Migration0011) — getrennt vonscans(Redirect-Worker). Die aktuelle Implementierung pseudonymisiert IP-Adressen mit HMAC-SHA-256 aus langlebigem Secret, Zweck und UTC-Tag; Original-IP-Adressen erreichen nie D1. - Dashboard: Mini-Chart-Karte (SVG, keine Chart-Library) auf
/dashboard/dpp/:dppIdmit 30-Tage-Balken + Top-3-Breakdowns. Empty-State sobald ein DPP live ist aber noch keine Scans hatte.
2026-04 · Live-EU-Compliance-Simulator (Q3.3.7)
- Neu:
POST /v1/dpp/:id/validate-update— simuliert Teil-Updates stateless (Status, Markt-Liste, …) ohne Persistenz. Antwort enthälteu_compliance+preview.changed_fields. - Dashboard: Simulator-Karte im DPP-Detail (
/dashboard/dpp/:dppId) — Chips fürDE/AT/FR/IT/ES/NL+ Custom, Status-Dropdown, Preview EU impact / Save changes / Reset. Non-blocking via RemixuseFetcher. - Härtung: Ausgelagerte Simulator-Helper (
readUpdatePatchFromForm,marketCountriesKey) + 18 neue Unit-Tests; Bugfix: Lone-Non-ISO-Input löscht die Marktliste nicht mehr.
2026-04 · Live-EU-Compliance-Preview im Create-Formular (Q3.3.6)
- Geändert:
POST /v1/dpp/validateliefert zusätzlicheu_compliance— derselbe Validator wieGET /v1/dpp/:id/eu-compliance, stateless vor dem Speichern. - Dashboard: Preview unter dem bestehenden Validation-Panel + neuer Save-Guard-Banner vor den Submit-Buttons, wenn Errors/Warnings offen sind (i18n-Pluralisierung DE/EN).
2026-04 · EU-Validator + Textil-UI (Q3.3.4 + Q3.3.5)
- Neu: EU-Compliance-Validator mit 5 Textil-Regeln (
TEXTILE_AGEC_REQUIRED,TEXTILE_MICROPLASTICS_CONSISTENCY,TEXTILE_SVHC_THRESHOLD,TEXTILE_GREENWASHING,TEXTILE_ESPR_READY). - Neu:
GET /v1/dpp/:id/eu-compliancemitcompliant/espr_ready/issues[]/summary. - Dashboard: EU-Compliance-Section im DPP-Detail (Summary-Tiles, gruppierte Issue-Karten, ESPR-Ready-Badge im Header).
2026-04 · Textil-DPP-Schema (Q3.3.1–Q3.3.3)
- Neu: Kategorie
textilemit AGEC-Pflichtchain (Weben/Stricken → Färben/Drucken → Konfektion), pro Faserorigin_country+recycled_pct,svhc_substances[], ESPR-Opt-in (PEF, Lebensdauer, Recyclability). - Neu: Basisfeld
market_countries: string[](ISO 3166-1 alpha-2) auf allen DPP-Kategorien — steuert FR-spezifische AGEC-Regeln und die französische Consumer-Pflichtnotiz. - Neu: Consumer-HTML-Template mit AGEC-Mikroplastik-Warnbox, 3-stufiger Herkunftskette (Flag-Pills), SVHC-Liste, Durability- und Recyclability-Section.
- Migration:
0010_dpp_market_countries(D1).
2026-04 · DPP-Bulk-Import (Q3.2.1–Q3.2.5)
- Neu:
POST /v1/dpp/importakzeptiert CSV und XLSX (Worker-kompatibel via SheetJSxlsx, ~283 KB gzip Bundle). - Skaliert: planbasiertes Limit (Free 100 → Enterprise 10k) + chunked
db.batch()à 100 + 5 MB Body-Limit. - Neu: Fehlerreport als CSV im
errors_csv-Feld der 201-Antwort;GET /v1/dpp/import/templates/:category?format=csv|xlsxliefert fertige Vorlagen für Batterie und Textil. - Dashboard: Drag-and-Drop-Upload unter
/dashboard/dpp/importmit Template-Proxy und Inline-CSV-Download.
Nicht-Breaking — LTS-Erweiterungen
Alle oben genannten Änderungen sind additiv:
- Bestehende
POST /v1/dpp/validate-Clients ignorieren das neueeu_compliance-Feld ohne Änderung. - Bestehende
battery-Flows sind unverändert. market_countriesist optional und defaultet auf[].
Siehe API-Versionierung für die Breaking-Change-Policy.