GDPR e privacy
Privacy by Design
qr3.app è progettato fin dall’inizio per essere conforme al GDPR:
- Pseudonimizzazione dell’IP — Gli indirizzi IP vengono immediatamente convertiti in pseudonimi HMAC-SHA-256 del giorno UTC nell’Edge Worker. L’IP originale non viene mai memorizzato; lo pseudonimo resta un dato personale.
- Minimizzazione dei dati — Vengono raccolti solo i dati strettamente necessari al funzionamento.
API Privacy
Riepilogo dei dati (Art. 15 GDPR)
GET /v1/account/privacy — Restituisce una panoramica di tutti i dati memorizzati.
curl https://qr3.app/v1/account/privacy \ -H "Authorization: Bearer qr3_sk_..."Risposta:
{ "data": { "workspace_id": "ws_xxx", "organization_id": "org_xxx", "account": { "name": "Alex Example", "locale": "en", "created_at": "2026-01-15T09:00:00.000Z" }, "stored_data": { "qr_codes": 42, "scan_records": 15000, "dpp_scan_records": 3200, "dashboard_activity_records": 0, "dashboard_document_activity_records": 36, "api_operation_records": 180, "webhook_delivery_records": 640, "api_keys": 2, "webhooks": 1, "dpp_passports": 12, "dpp_documents": 30, "files": 4, "gs1_identifiers": 2, "gs1_verification_requests": 1, "code_comments": 5, "audit_log_entries": 0, "dpp_import_jobs": 1, "workspace_members": 3, "scan_quota_alerts": 1 }, "gdpr_rights": { "access": "GET /v1/account/privacy", "export": "GET /v1/account/export", "erasure": "DELETE /v1/account", "consents": "GET /v1/account/privacy/consents" }, "data_processing": { "ip_storage": "Purpose-separated HMAC-SHA-256 UTC-day IP pseudonym — pseudonymous personal data", "scan_retention": "90 days by default", "usage_retention": "90 UTC days", "webhook_delivery_retention": "90 UTC days", "pii_in_export": true, "erasure": "DELETE /v1/account erases the person's account: organizations they own with all workspaces and data (unless another org admin takes them over), their memberships in other organizations, their login and their Stripe customer. A running subscription ends immediately, without refund.", "backup_retention": "Erased rows stay restorable from the database backups (Cloudflare D1 Time Travel) for up to 30 days, then they are gone.", "billing_records": "Invoices stay with Stripe for the statutory retention period (7 years, § 132 BAO)." } }, "meta": { "request_id": "req_xxx" }}stored_data conta le voci memorizzate. scan_records sono le scansioni dei QR Code, dpp_scan_records le richieste ai passaporti del prodotto tramite il resolver DPP pubblico. Entrambi contengono lo pseudonimo IP.
dashboard_activity_records e dashboard_document_activity_records contano i giorni in cui un membro ha aperto la dashboard (due tabelle), api_operation_records i contatori giornalieri delle richieste per chiave API, operazione ed esito, e webhook_delivery_records i tentativi di consegna dei webhook, compresi quelli dei webhook eliminati. Nessuno di essi memorizza un indirizzo IP o uno user-agent; il log di consegna memorizza però il payload dell’evento e il primo kilobyte (1 KB) della risposta del destinatario. Tutti e quattro vengono eliminati dopo 90 giorni UTC (usage_retention, webhook_delivery_retention).
account indica la persona dietro la richiesta — nella dashboard la persona che ha effettuato l’accesso, altrimenti la persona che ha creato la chiave — con e-mail, nome, lingua e data di creazione; per una Publishable Key il valore è null. Gli altri contatori coprono ogni altra tabella con dati dell’account: passaporti del prodotto, documenti DPP, file, identificatori GS1, commenti, voci di audit, esecuzioni di importazione, membri del workspace e avvisi di quota dell’organizzazione (scan_quota_alerts). Le righe eliminate logicamente vengono conteggiate, perché sono ancora memorizzate. erasure, backup_retention e billing_records descrivono cosa elimina DELETE /v1/account e cosa resta per un certo tempo; pii_in_export è true perché l’esportazione indica la persona (e-mail, nome) — gli pseudonimi IP non lasciano mai il database.
Esportazione dei dati (Art. 20 GDPR — Portabilità dei dati)
GET /v1/account/export — Scarica tutti i dati come file JSON.
curl https://qr3.app/v1/account/export \ -H "Authorization: Bearer qr3_sk_..." \ -o my-data.jsonL’esportazione contiene:
- Tutti i QR Code (inclusi quelli eliminati)
- Statistiche di scansione aggregate (nessun hash IP grezzo)
- Richieste DPP aggregate per passaporto del prodotto (
dpp_scan_aggregates, anche qui senza hash IP) - Statistiche di utilizzo aggregate (
dashboard_activity_aggregates,api_operation_aggregates): giorni attivi e numero di utenti attivi per canale della dashboard, contatori delle richieste per operazione ed esito, senza ID di utenti e chiavi - Tentativi di consegna dei webhook per webhook e tipo di evento (
webhook_delivery_aggregates), senza payload e risposta - La persona dietro la richiesta (
account), l’organizzazione (organization; indirizzo di fatturazione e di notifica solo per gli amministratori dell’organizzazione) e il workspace (workspace) - Passaporti del prodotto, metadati dei documenti DPP e dei file (non i file stessi), identificatori GS1, richieste di verifica degli stessi ed esecuzioni di importazione (
dpp_passports,dpp_documents,files,gs1_identifiers,gs1_verification_requests,dpp_import_jobs) - Commenti senza campi autore (
code_comments), voci di audit come aggregati (audit_log_aggregates) e membri per ruolo (member_aggregates) - Chiavi API senza ID e hash (
api_keys), webhook senza URL e segreto (webhooks) e gli avvisi di quota dell’organizzazione (scan_quota_alerts) - Timestamp di creazione e modifica
Le Publishable Key (qr3_pk_) ricevono 403: il file contiene gli indirizzi di destinazione e le note di tutti i codici.
Eliminazione dell’account (Art. 17 GDPR — Diritto all’oblio)
DELETE /v1/account — Elimina l’account della persona dietro la richiesta, immediatamente e in modo irreversibile: nella dashboard la persona che ha effettuato l’accesso, altrimenti la persona che ha creato la chiave. Una richiesta di cancellazione via e-mail porta alla stessa eliminazione: il gestore elimina l’accesso presso Clerk e poi qr3 elimina l’account.
curl -X DELETE https://qr3.app/v1/account \ -H "Authorization: Bearer qr3_sk_..."Cosa succede:
- Le organizzazioni che appartengono alla persona vengono eliminate con tutti i workspace, i codici, i passaporti del prodotto, i file, le statistiche e i log, compresi i loro file nello storage. Se un’organizzazione di questo tipo ha un altro amministratore dell’organizzazione attivo, la rileva invece quello con più anzianità, con i dati e l’abbonamento.
- Abbonamento: un abbonamento in corso termina immediatamente, senza rimborso proporzionale e senza fattura finale; poi il cliente Stripe viene eliminato. Le fatture restano presso Stripe per il periodo legale di conservazione (7 anni, § 132 BAO).
- Team: le appartenenze della persona ad altre organizzazioni terminano; quei team conservano i propri dati. I commenti della persona non indicano più un autore, le sue voci di audit perdono pseudonimo IP e user agent, e le chiavi API che ha creato lì vengono revocate.
- La persona: l’accesso (Clerk) e il record utente vengono eliminati.
La risposta è 200 con status: "completed" e i contatori organizations_deleted, organizations_transferred, memberships_removed e login_deleted. Per organizzazioni molto grandi una richiesta non basta: la risposta è allora 202 con status: "in_progress", e l’eliminazione si completa automaticamente entro poche ore.
Richiede una Secret Key o Test Key con diritto di scrittura, oppure una sessione della dashboard (qualsiasi ruolo, perché si tratta del proprio account). Le Publishable Key e le chiavi di sola lettura ricevono 403, una chiave senza una persona dietro (created_by = 'system') riceve 409. 503 significa che al deployment manca un segreto necessario all’eliminazione; non è stato eliminato nulla.
Gestione dei consensi
GET /v1/account/privacy/consents — Recupera i consensi attuali.
POST /v1/account/privacy/consents — Aggiorna i consensi.
# Recuperare i consensi attualicurl https://qr3.app/v1/account/privacy/consents \ -H "Authorization: Bearer qr3_sk_..."
# Disattivare le e-mail di marketingcurl -X POST https://qr3.app/v1/account/privacy/consents \ -H "Authorization: Bearer qr3_sk_..." \ -H "Content-Type: application/json" \ -d '{ "marketing_emails": false, "analytics": true, "product_updates": true }'Campi di consenso:
| Campo | Predefinito | Descrizione |
|---|---|---|
marketing_emails | false | Newsletter ed e-mail promozionali |
analytics | true | Statistiche di utilizzo aggregate |
product_updates | true | Novità sui prodotti e changelog |
Modificare i consensi richiede i permessi di scrittura: il ruolo viewer e le Publishable Key ricevono 403.
Implementazione tecnica del GDPR
Pseudonimizzazione dell’IP (Art. 25 GDPR)
Richiesta HTTP → Cloudflare Edge Worker ↓Intestazione CF-Connecting-IP → HMAC-SHA-256(IP_HASH_SALT, scopo, giorno UTC, IP) ↓ip_hash (pseudonimo giornaliero) → database D1 ↓L'IP originale non viene mai memorizzatoIP_HASH_SALT è un segreto maestro di lunga durata; lo scopo e il giorno UTC derivano automaticamente pseudonimi giornalieri separati. Gli pseudonimi di giorni UTC diversi non sono direttamente uguali, ma chi possiede il segreto può ricalcolare un candidato IP noto per un giorno specifico. Non sono usati né un salt giornaliero in KV né una rotazione manuale quotidiana.
Conservazione dei dati (automatizzata)
| Piano | Conservazione |
|---|---|
| Tutti i piani | 90 giorni |
Anche le statistiche di utilizzo (attività della dashboard, contatori di utilizzo dell’API) e i log di consegna dei webhook vengono conservati per 90 giorni UTC: il giorno UTC in corso e gli 89 precedenti. Lo stesso job cron giornaliero elimina le righe più vecchie (purgeOldDashboardActivity, purgeOldApiOperationUsage, purgeOldWebhookDeliveries).
DPA dei sub-responsabili
Cloudflare, Inc. tratta i dati in qualità di responsabile del trattamento. La DPA è disponibile all’indirizzo cloudflare.com/cloudflare-customer-dpa.
Cloudflare tratta i dati con un’indicazione della posizione geografica (location hint) riferita alla regione UE (tra cui Francoforte); una giurisdizione UE contrattualmente garantita non è attualmente prenotata. Poiché Cloudflare, Inc. ha sede negli USA, si applicano le clausole contrattuali standard (SCC) ai sensi dell’Art. 46 GDPR come base per il trasferimento.
Contatti
- Responsabile della protezione dei dati (DPO): [email protected]
- Accordo sul trattamento dei dati (DPA): [email protected] (su richiesta)
- Informativa sulla privacy: Informativa sulla privacy