Hoppa till innehåll

GDPR & dataskydd

Dataskydd genom design

qr3.app är byggt från grunden för att vara GDPR-kompatibelt:

  • IP-pseudonymisering — IP-adresser omvandlas omedelbart till HMAC-SHA-256-UTC-dagspseudonymer i Edge-workern. Den ursprungliga IP-adressen lagras aldrig; pseudonymen är fortfarande en personuppgift.
  • Dataminimering — Endast de uppgifter som är nödvändiga för driften samlas in.

Privacy-API

Sammanfattning av personuppgifter (Art. 15 GDPR)

GET /v1/account/privacy — Returnerar en översikt över alla sparade uppgifter.

Terminal window
curl https://qr3.app/v1/account/privacy \
-H "Authorization: Bearer qr3_sk_..."

Svar:

{
"data": {
"workspace_id": "ws_xxx",
"organization_id": "org_xxx",
"account": {
"email": "[email protected]",
"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 räknar de sparade posterna. scan_records är skanningar av QR-koder, dpp_scan_records är förfrågningar till produktpass via den offentliga DPP-resolvern. Båda innehåller IP-pseudonymet.

dashboard_activity_records och dashboard_document_activity_records räknar de dagar då en medlem öppnade dashboarden (två tabeller), api_operation_records de dagliga anropsräknarna per API-nyckel, operation och utfall, och webhook_delivery_records leveransförsöken för webhooks, även för raderade webhooks. Ingen av dem lagrar någon IP-adress eller User-Agent; leveransloggen lagrar däremot händelsens payload och de första 1 KB av mottagarens svar. Alla fyra raderas efter 90 UTC-dagar (usage_retention, webhook_delivery_retention).

account anger personen bakom begäran — i dashboarden den inloggade personen, annars den person som skapade nyckeln — med e-post, namn, språk och skapandedatum; för en Publishable Key är värdet null. De övriga räknarna täcker varje ytterligare tabell med kontots data: produktpass, DPP-dokument, filer, GS1-identifierare, kommentarer, granskningsposter, importkörningar, workspace-medlemmar och organisationens kvotmeddelanden (scan_quota_alerts). Mjukt raderade rader räknas, eftersom de fortfarande är lagrade. erasure, backup_retention och billing_records beskriver vad DELETE /v1/account raderar och vad som finns kvar en tid; pii_in_export är true eftersom exporten anger personen (e-post, namn) — IP-pseudonymer lämnar aldrig databasen.


Dataexport (Art. 20 GDPR — dataportabilitet)

GET /v1/account/export — Laddar ner alla data som en JSON-fil.

Terminal window
curl https://qr3.app/v1/account/export \
-H "Authorization: Bearer qr3_sk_..." \
-o my-data.json

Exporten innehåller:

  • Alla QR-koder (inklusive raderade)
  • Aggregerad skanningsstatistik (inga råa IP-hashar)
  • Aggregerade DPP-förfrågningar per produktpass (dpp_scan_aggregates, inte heller några IP-hashar)
  • Användningsstatistik som aggregat (dashboard_activity_aggregates, api_operation_aggregates): aktiva dagar och antal aktiva användare per dashboardkanal, anropsräknare per operation och utfall — utan användar- och nyckel-ID:n
  • Leveransförsök för webhooks per webhook och händelsetyp (webhook_delivery_aggregates), utan payload och svar
  • Personen bakom begäran (account), organisationen (organization; fakturerings- och aviseringsadress endast för organisationsadministratörer) och workspacet (workspace)
  • Produktpass, metadata för DPP-dokument och filer (inte själva filerna), GS1-identifierare, ansökningar om att verifiera dem och importkörningar (dpp_passports, dpp_documents, files, gs1_identifiers, gs1_verification_requests, dpp_import_jobs)
  • Kommentarer utan författarfält (code_comments), granskningsposter som aggregat (audit_log_aggregates) och medlemmar per roll (member_aggregates)
  • API-nycklar utan ID och hash (api_keys), webhooks utan URL och hemlighet (webhooks) och organisationens kvotmeddelanden (scan_quota_alerts)
  • Tidsstämplar för skapande och ändring

Publishable Keys (qr3_pk_) får 403: filen innehåller måladresser och anteckningar för alla koder.


Radera konto (Art. 17 GDPR — rätten att bli bortglömd)

DELETE /v1/account — Raderar kontot för personen bakom begäran, omedelbart och oåterkalleligt: i dashboarden den inloggade personen, annars den person som skapade nyckeln. En raderingsbegäran via e-post leder till samma radering: operatören raderar inloggningen hos Clerk, och därefter raderar qr3 kontot.

Terminal window
curl -X DELETE https://qr3.app/v1/account \
-H "Authorization: Bearer qr3_sk_..."

Vad som händer:

  1. Organisationer som tillhör personen raderas med alla workspaces, koder, produktpass, filer, statistik och loggar, inklusive deras filer i lagringen. Har en sådan organisation en annan aktiv organisationsadministratör tar i stället den som varit administratör längst över organisationen med data och prenumeration.
  2. Prenumeration: en pågående prenumeration upphör omedelbart, utan proportionell återbetalning och utan slutfaktura; därefter raderas Stripe-kunden. Fakturor finns kvar hos Stripe under den lagstadgade lagringstiden (7 år, § 132 BAO).
  3. Team: personens medlemskap i andra organisationer upphör; dessa team behåller sina data. Personens kommentarer anger inte längre någon författare, personens granskningsposter förlorar IP-pseudonym och user agent, och de API-nycklar som personen skapade där återkallas.
  4. Personen: inloggningen (Clerk) och användarposten raderas.

Svaret är 200 med status: "completed" och räknarna organizations_deleted, organizations_transferred, memberships_removed och login_deleted. För mycket stora organisationer räcker inte en begäran: svaret är då 202 med status: "in_progress", och raderingen slutförs automatiskt inom några timmar.

Kräver en Secret Key eller Test Key med skrivbehörighet eller en dashboard-session (vilken roll som helst, eftersom det gäller personens eget konto). Publishable Keys och skrivskyddade nycklar får 403, en nyckel utan person bakom (created_by = 'system') får 409. 503 betyder att driftsättningen saknar en hemlighet som raderingen behöver; ingenting har raderats.


Hantera samtycken

GET /v1/account/privacy/consents — Hämta aktuella samtycken.

POST /v1/account/privacy/consents — Uppdatera samtycken.

Terminal window
# Hämta aktuella samtycken
curl https://qr3.app/v1/account/privacy/consents \
-H "Authorization: Bearer qr3_sk_..."
# Inaktivera marknadsföringsmejl
curl -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 }'

Samtyckesfält:

FältStandardBeskrivning
marketing_emailsfalseNyhetsbrev och reklammejl
analyticstrueAggregerad användningsstatistik
product_updatestrueProduktnyheter och ändringsloggar

Att ändra samtycken kräver skrivbehörighet: rollen viewer och Publishable Keys får 403.


Teknisk GDPR-implementering

IP-pseudonymisering (Art. 25 GDPR)

HTTP-begäran → Cloudflare Edge Worker
↓
CF-Connecting-IP-rubrik → HMAC-SHA-256(IP_HASH_SALT, syfte, UTC-dag, IP)
↓
ip_hash (daglig pseudonym) → D1-databas
↓
Den ursprungliga IP-adressen lagras aldrig

IP_HASH_SALT är en långlivad huvudhemlighet; syfte och UTC-dag härleder automatiskt separata dagliga pseudonymer. Pseudonymer från olika UTC-dagar är inte direkt lika, men innehavaren av hemligheten kan beräkna om en känd IP-kandidat för en viss dag. Varken KV-dagssalt eller manuell daglig rotation används.

Datalagring (automatiserad)

AbonnemangLagringstid
Alla abonnemang90 dagar

Även användningsstatistik (dashboard-aktivitet, API-användningsräknare) och webhook-leveransloggar sparas i 90 UTC-dagar: den aktuella UTC-dagen och de 89 föregående dagarna. Samma dagliga cron-jobb raderar äldre rader (purgeOldDashboardActivity, purgeOldApiOperationUsage, purgeOldWebhookDeliveries).

Underbiträdesavtal (DPA)

Cloudflare, Inc. behandlar uppgifter som personuppgiftsbiträde. Underbiträdesavtalet (DPA) finns tillgängligt på cloudflare.com/cloudflare-customer-dpa.

Cloudflare behandlar uppgifter med en platshänvisning till EU-regionen (bland annat Frankfurt); en avtalsenligt garanterad EU-jurisdiktion är för närvarande inte bokad. Eftersom Cloudflare, Inc. är baserat i USA tillämpas standardavtalsklausuler (SCC) i enlighet med art. 46 GDPR som överföringsmekanism.


Kontakt