Saltearse al contenido

Changelog

Changelog

Destacados seleccionados de los últimos lanzamientos. Para ver las versiones completas de la API y la política de cambios disruptivos, consulte el Versionado de API y política LTS.

Cambios detallados en endpoints individuales: Especificación OpenAPI y referencia interactiva de la API.


2026-08 · Cursor keyset ahora en todos los endpoints de lista

  • Corrección: La corrección del cursor para GET /v1/codes y GET /v1/dpp (ver más abajo) se ha implementado ahora en todas las demás listas paginadas por cursor: GET /v1/qr-codes/:id/comments, /v1/workspaces, /v1/gs1/identifiers, /v1/members, /v1/audit-logs, /v1/admin/orgs, /v1/admin/users y /v1/webhooks/:id/deliveries. Todas paginaban únicamente a través de created_at; las filas con marcas de tiempo idénticas (entradas de auditoría de una operación por lotes, reintentos de webhook, importaciones de miembros) podían perderse en la página siguiente. La clave de ordenación ahora es la tupla (created_at, id) en todas partes.
  • Cambio en la API: En estas listas, meta.pagination.next_cursor es a partir de ahora también un valor opaco (base64url) en lugar de una marca de tiempo simple; los cursores ilegibles devuelven 400, y los antiguos cursores de marca de tiempo se seguirán aceptando de forma transitoria. Excepción GET /v1/webhooks/:id/deliveries: allí next_cursor sigue siendo el ID de la última entrega (ID desconocido → primera página). Los clientes que devuelven next_cursor sin cambios — Dashboard, CLI, SDKs, MCP — no necesitan cambiar nada.
  • Impacto: No se requiere migración. Si ha echado en falta entradas al paginar en alguna de estas listas (por ejemplo, el registro de auditoría o el registro de entregas en el Dashboard): nunca desaparecieron; las listas las muestran por completo a partir de ahora.

2026-08 · Las marcas de tiempo vuelven a ser conformes con OpenAPI tras la edición y eliminación

  • Corrección: Tras PATCH /v1/codes/{id}, updated_at se devolvía en formato SQLite sin zona horaria (2026-08-17 09:00:00), aunque la especificación de OpenAPI promete format: date-time y la creación (POST) proporciona la marca de tiempo ISO (2026-08-17T09:00:00.000Z). Lo mismo se aplicaba a deleted_at/updated_at en el soft-delete, así como a las rutas de edición y eliminación de claves de API, organizaciones, workspaces, miembros, comentarios y webhooks, además de a last_used_at de claves de API y last_triggered_at de webhooks. Todas las rutas de escritura registran ahora en formato ISO 8601 (UTC, T y Z).
  • Impacto: Los clientes que analizan updated_at con new Date(...) (SDKs, CLI, Dashboard) leían el formato con espacio como hora local — la CLI mostraba la hora desplazada por el desfase local en los códigos editados una vez (Viena: −2 h). Esto se ha solucionado. Además, una migración de datos normaliza los valores ya almacenados en el formato antiguo a ISO, para que la ordenación y las comparaciones en conjuntos de datos mixtos sean correctas. Sin cambios en los nombres de los campos ni en la estructura de la respuesta.
  • Contexto: La misma clase de error que las dos correcciones de abajo (expiración de claves de API, límite de reescaneo): datetime('now') de SQLite escribe YYYY-MM-DD HH:MM:SS, mientras que todos los demás escritores utilizan ISO 8601. Un test de protección en el código fuente evitará que esto vuelva a ocurrir en el futuro.

2026-08 · La paginación de listas ya no pierde códigos batch

  • Corrección: GET /v1/codes y GET /v1/dpp paginaban únicamente a través de created_at. Sin embargo, los códigos de POST /v1/codes/batch, POST /v1/dpp/batch y de la importación CSV/XLSX comparten una sola marca de tiempo; tan pronto como un lote era mayor que limit (por defecto 20), la segunda página ya no entregaba las filas restantes de esa misma marca de tiempo. Los códigos existían y se podía acceder a ellos a través de GET /v1/codes/:id, pero nunca aparecían en la lista (Dashboard, CLI qr3 list, SDKs, MCP). El cursor ahora es un Keyset sobre (created_at, id).
  • Cambio en la API: meta.pagination.next_cursor es a partir de ahora un valor opaco (base64url) en lugar de una marca de tiempo simple. Quienes devuelvan el cursor sin cambios como ?cursor= — tal como lo hacen el Dashboard, la CLI, todos los SDKs y el servidor MCP — no tienen que cambiar nada. Los antiguos cursores de marca de tiempo se seguirán aceptando de forma transitoria; los cursores ilegibles ahora devuelven 400 en lugar de entregar silenciosamente la primera página.
  • Impacto: Si después de una importación por lotes veía menos códigos en la lista de los que se habían creado: los códigos nunca desaparecieron; la lista ahora los muestra por completo. No se requiere migración.

2026-08 · Los re-escaneos de seguridad vuelven a ejecutarse cada 24 horas

  • Corrección: El re-escaneo periódico de las URLs de destino y los enlaces de la página de destino (Google Web Risk) omitía códigos cuyo último escaneo se encontraba en el mismo día calendario que el límite de 24 horas; dependiendo de la hora, el re-escaneo se retrasaba hasta un día más. El límite ahora se calcula en el mismo formato ISO en el que se almacenan las marcas de tiempo de escaneo.
  • Impacto: Una URL de destino que sea clasificada como no segura después del último escaneo volverá a provocar la pausa automática del código dentro de la ventana documentada de 24 horas. Sin cambios en la API ni en el formato de respuesta.

2026-08 · Las claves de API expiran en el momento exacto de su expiración

  • Corrección: Una clave de API cuyo expires_at coincidía con el mismo día se seguía aceptando hasta la medianoche UTC. Ahora, la expiración se compara como una marca de tiempo en lugar de como una cadena de texto; una clave expirada devuelve inmediatamente 401.
  • Contexto: expires_at se almacena como una marca de tiempo ISO (2026-08-14T09:00:00Z), mientras que el lado de la comparación proporcionaba el formato con espacio (2026-08-14 09:00:00). Por lo tanto, la comparación directa de cadenas de texto solo funcionaba correctamente si la fecha ya era diferente.
  • Impacto: No se requiere migración, el formato de respuesta de GET /v1/api-keys permanece sin cambios. Los valores de expiración ilegibles ahora se consideran expirados en lugar de válidos.

2026-08 · Referencia de la API: Gestión de inquilinos documentada

  • OpenAPI: La especificación — y con ella la referencia interactiva — ahora documenta organizaciones (incl. GET /v1/organizations/usage), workspaces, miembros y roles y registros de auditoría.
  • Billing: El resumen de tarifas (GET /v1/billing/plans) es público; el checkout (POST /v1/billing/checkout) y el portal de clientes de Stripe (GET /v1/billing/portal) están designados como endpoints para administradores de la organización.
  • Exportación de escaneos: Las estadísticas de escaneo (GET /v1/codes/{id}/scans) y la exportación de datos brutos (…/scans.csv, …/scans.xlsx) están completamente documentadas — incluyendo el aviso de RGPD: el ip_hash nunca está incluido en la exportación.
  • Comportamiento de errores: También se ha documentado la respuesta 400 de la validación de solicitudes: el cuerpo es el error de Zod sin procesar, no un documento de problema RFC-7807 — sin embargo, se entrega bajo el Content-Type application/problem+json.

2026-08 · Copiar enlaces públicos de archivos

  • Dashboard: los archivos públicos en la página de detalles de un código ahora tienen un botón que copia su enlace público al portapapeles — listo para usarse como URL de destino de un código QR cuando un escaneo deba abrir de inmediato un documento concreto en lugar de la página de destino con la lista de archivos.
  • API: los endpoints de archivos (/v1/files) ahora devuelven también public_url. El campo solo está definido en archivos con visibility: public — los archivos privados no tienen dirección pública.
  • Comportamiento: el enlace no necesita inicio de sesión y abre el archivo directamente en el navegador. Reemplazar el archivo no modifica el enlace, de modo que un código impreso con él sigue siendo válido. Detalles: Archivos y fichas técnicas.

2026-07 · Roles de equipo: Colaborador sin eliminar y facturación de Administrador

  • Nuevo: rol de miembro Colaborador (sin eliminar) — crea y edita códigos QR, archivos y Digital Product Passports, pero no puede eliminar nada ni crear claves de API. Todos los endpoints destructivos comprueban el rol en el servidor (403).
  • Facturación: las mejoras de plan y el portal de clientes de Stripe (POST /v1/billing/checkout, GET /v1/billing/portal) ahora están reservados a los Administradores de la organización — todos los demás roles ven un resumen de plan de solo lectura.
  • Dashboard: las acciones no permitidas para el rol actual se ocultan: un Lector no ve, por ejemplo, botones para crear, editar o eliminar; las listas, descargas y estadísticas siguen visibles. Detalles: Equipo y roles.

2026-06 · Enlaces externos en la página de destino del código

  • Página de destino: La página de destino alojada por qr3 de un código ahora puede listar enlaces externos autoalojados ({ label, url }) además de o en lugar de los archivos subidos, por ejemplo para fichas técnicas en tu propio sitio.
  • API: POST/PATCH /v1/codes aceptan un array links (0–20 entradas, http(s), ≤ 2048 caracteres). Cada URL se verifica con Google Web Risk; una URL no segura devuelve 422. Un array vacío elimina todos los enlaces.
  • Dashboard: Añade, reordena y elimina enlaces en la página de detalles del código.
  • Seguridad: Los enlaces renderizados permanecen seguros frente a XSS (escapados, solo http(s)) y la página mantiene su encabezado noindex.

2026-04 · Analíticas del Dashboard por código QR

  • Dashboard: El botón de analíticas en la lista de códigos QR ahora abre la página de estadísticas del código QR correspondiente en /dashboard/codes/{id}.
  • Routing: El alias /dashboard/codes sigue redirigiendo a /dashboard, pero ya no intercepta rutas de detalle como /dashboard/codes/{id}.
  • API: La página de detalles carga el código QR directamente mediante GET /v1/codes/:id; de este modo, ya no depende de los límites de paginación de la lista.
  • Tests: Las pruebas de regresión cubren la redirección del alias y la carga directa del código.

2026-04 · Diálogo de eliminación en el Dashboard para códigos QR

  • Dashboard: El icono de papelera en la lista de códigos QR ahora abre un diálogo de React propio en lugar de una ventana emergente nativa del navegador.
  • Feedback: Después de la eliminación, aparece una notificación toast de éxito o error.
  • Tests: packages/dashboard/tests/dashboard.test.ts evita regresiones en confirm() en el flujo de eliminación de códigos QR.

2026-04 · Prueba de enlace corto en el Dashboard para códigos QR dinámicos

  • Dashboard: Los shortcodes en la lista de códigos QR ahora se pueden hacer clic directamente como enlaces de redirección externos. El icono de enlace externo junto a, por ejemplo, wu3qaa abre https://qr3.app/{shortCode} en una nueva pestaña.
  • i18n: Se han añadido textos de información sobre herramientas (tooltips) para alemán e inglés.
  • Tests: packages/dashboard/tests/dashboard.test.ts protege el href del enlace, el comportamiento de nueva pestaña, noopener noreferrer y el icono contra regresiones.

2026-04 · Ruta del Redirect-Worker para códigos QR dinámicos

  • Corrección: Los códigos QR dinámicos en https://qr3.app/{shortCode} vuelven a ser procesados por el Redirect-Worker. La ruta de producción ahora utiliza qr3.app/* porque las rutas de Cloudflare Workers no admiten parámetros de ruta :code.
  • Robustez: Las rutas que no coinciden se pasan al origen de la landing page para que las páginas normales como /de/pricing no sean bloqueadas por el Redirect-Worker.
  • Tests: packages/redirect/tests/unit/redirect.test.ts verifica la ruta comodín (wildcard), el procesamiento de shortcodes y el paso directo al origen (pass-through).

2026-04 · Resumen de escaneos DPP del Workspace (Q3.4.2)

  • Nuevo: GET /v1/workspace/stats/dpp?days=30 — agrega todos los dpp_scans del Workspace de la clave API (active_dpps, scans_by_day, top_dpps con nombre de producto/categoría).
  • Dashboard: Tarjeta en la página de inicio (/dashboard) con gráfico de barras de 30 días + listas principales — en paralelo a las tarjetas de códigos QR.
  • Public: Enlace corto de marketing GET /dpp/dpp_<id> (un segmento) para demostraciones en vivo, en paralelo a /dpp/{gtin}/{serial}.

2026-04 · Analíticas de escaneos DPP (Q3.4.1)

  • Nuevo: GET /v1/dpp/:id/stats?days=30 — escaneos agregados del resolver público GS1 por DPP. Campos: total_scans, period_scans, scans_by_day, top_countries, top_devices, top_representations.
  • Nuevo: Tabla dpp_scans (migración 0011) — separada de scans (Redirect-Worker). Las direcciones IP se cifran con un hash utilizando una sal (salt) rotativa diaria, las IP en bruto nunca llegan a D1.
  • Dashboard: Tarjeta de minigráfico (SVG, sin biblioteca de gráficos) en /dashboard/dpp/:dppId con barras de 30 días + desgloses de los 3 principales. Estado vacío (empty state) tan pronto como un DPP esté activo pero aún no haya tenido escaneos.

2026-04 · Simulador de conformidad de la UE en vivo (Q3.3.7)

  • Nuevo: POST /v1/dpp/:id/validate-update — simula actualizaciones parciales sin estado (stateless) (estado, lista de mercados, …) sin persistencia. La respuesta contiene eu_compliance + preview.changed_fields.
  • Dashboard: Tarjeta de simulador en el detalle del DPP (/dashboard/dpp/:dppId) — etiquetas (chips) para DE/AT/FR/IT/ES/NL + personalizadas, menú desplegable de estado, Preview EU impact / Save changes / Reset. No bloqueante a través de Remix useFetcher.
  • Robustez: Funciones auxiliares del simulador externalizadas (readUpdatePatchFromForm, marketCountriesKey) + 18 nuevas pruebas unitarias; corrección de errores: la entrada única no ISO ya no borra la lista de mercados.

2026-04 · Vista previa de conformidad de la UE en vivo en el formulario de creación (Q3.3.6)

  • Modificado: POST /v1/dpp/validate ahora también devuelve eu_compliance — el mismo validador que GET /v1/dpp/:id/eu-compliance, sin estado (stateless) antes de guardar.
  • Dashboard: Vista previa debajo del panel de validación existente + nuevo banner de protección de guardado antes de los botones de envío si hay errores/advertencias pendientes (pluralización i18n DE/EN).

2026-04 · Validador de la UE + UI textil (Q3.3.4 + Q3.3.5)

  • Nuevo: Validador de conformidad de la UE con 5 reglas textiles (TEXTILE_AGEC_REQUIRED, TEXTILE_MICROPLASTICS_CONSISTENCY, TEXTILE_SVHC_THRESHOLD, TEXTILE_GREENWASHING, TEXTILE_ESPR_READY).
  • Nuevo: GET /v1/dpp/:id/eu-compliance con compliant / espr_ready / issues[] / summary.
  • Dashboard: Sección de conformidad de la UE en el detalle del DPP (mosaicos de resumen, tarjetas de problemas agrupadas, insignia ESPR-Ready en el encabezado).

2026-04 · Esquema DPP textil (Q3.3.1–Q3.3.3)

  • Nuevo: Categoría textile con cadena obligatoria AGEC (tejeduría/punto → teñido/estampado → confección), por fibra origin_country + recycled_pct, svhc_substances[], opción de exclusión voluntaria (opt-in) de ESPR (PEF, vida útil, reciclabilidad).
  • Nuevo: Campo básico market_countries: string[] (ISO 3166-1 alpha-2) en todas las categorías DPP — controla las reglas AGEC específicas de Francia y la nota obligatoria para el consumidor francés.
  • Nuevo: Plantilla HTML para el consumidor con cuadro de advertencia de microplásticos AGEC, cadena de origen de 3 niveles (píldoras con banderas), lista SVHC, sección de durabilidad y reciclabilidad.
  • Migración: 0010_dpp_market_countries (D1).

2026-04 · Importación masiva de DPP (Q3.2.1–Q3.2.5)

  • Nuevo: POST /v1/dpp/import acepta CSV y XLSX (compatible con Workers a través de SheetJS xlsx, ~283 KB de paquete comprimido con gzip).
  • Escalado: límite basado en el plan (Free 100 → Enterprise 10k) + db.batch() fragmentado (chunked) de 100 en 100 + límite de cuerpo de 5 MB.
  • Nuevo: Informe de errores en formato CSV en el campo errors_csv de la respuesta 201; GET /v1/dpp/import/templates/:category?format=csv|xlsx proporciona plantillas listas para usar para baterías y textiles.
  • Dashboard: Carga mediante arrastrar y soltar (drag-and-drop) en /dashboard/dpp/import con proxy de plantilla y descarga de CSV en línea.

Sin cambios disruptivos — Extensiones LTS

Todos los cambios mencionados anteriormente son aditivos:

  • Los clientes existentes de POST /v1/dpp/validate ignoran el nuevo campo eu_compliance sin necesidad de realizar cambios.
  • Los flujos de battery existentes permanecen sin cambios.
  • market_countries es opcional y tiene como valor predeterminado [].

Consulte Versionado de API para conocer la política de cambios disruptivos.