AVG & Privacy
Privacy-by-design
qr3.app is vanaf de basis AVG-conform gebouwd:
- IP-pseudonimisering — IP-adressen worden in de Edge-Worker direct omgezet in HMAC-SHA-256-UTC-dagpseudoniemen. Het oorspronkelijke IP-adres wordt nooit opgeslagen; het pseudoniem blijft een persoonsgegeven.
- Dataminimalisatie — Alleen de gegevens die noodzakelijk zijn voor de werking worden verzameld.
Privacy-API
Gegevenssamenvatting (Art. 15 AVG)
GET /v1/account/privacy — Retourneert een overzicht van alle opgeslagen gegevens.
curl https://qr3.app/v1/account/privacy \ -H "Authorization: Bearer qr3_sk_..."Response:
{ "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 telt de opgeslagen items. scan_records zijn scans van QR-codes, dpp_scan_records zijn opvragingen van productpaspoorten via de openbare DPP-resolver. Beide bevatten het IP-pseudoniem.
dashboard_activity_records en dashboard_document_activity_records tellen de dagen waarop een lid het dashboard heeft geopend (twee tabellen), api_operation_records de dagelijkse verzoektellers per API-key, operatie en uitkomst, en webhook_delivery_records de leveringspogingen van webhooks, inclusief die van verwijderde webhooks. Geen daarvan slaat een IP-adres of een User-Agent op; het leveringslog slaat wel de payload van de gebeurtenis en de eerste 1 KB van het antwoord van de ontvanger op. Alle vier worden na 90 UTC-dagen verwijderd (usage_retention, webhook_delivery_retention).
account noemt de persoon achter het verzoek — in het dashboard de ingelogde persoon, anders de persoon die de key heeft aangemaakt — met e-mail, naam, taal en aanmaakdatum; bij een Publishable Key is de waarde null. De overige tellers dekken elke verdere tabel met gegevens van het account: productpaspoorten, DPP-documenten, bestanden, GS1-identificatoren, opmerkingen, auditvermeldingen, importruns, workspaceleden en de quotameldingen van de organisatie (scan_quota_alerts). Zacht verwijderde rijen tellen mee, omdat ze nog zijn opgeslagen. erasure, backup_retention en billing_records beschrijven wat DELETE /v1/account verwijdert en wat nog enige tijd blijft; pii_in_export is true, omdat de export de persoon noemt (e-mail, naam) — IP-pseudoniemen verlaten de database nooit.
Gegevensexport (Art. 20 AVG — dataportabiliteit)
GET /v1/account/export — Downloadt alle gegevens als JSON-bestand.
curl https://qr3.app/v1/account/export \ -H "Authorization: Bearer qr3_sk_..." \ -o my-data.jsonDe export bevat:
- Alle QR-codes (inclusief verwijderde)
- Geaggregeerde scanstatistieken (geen ruwe IP-hashes)
- Geaggregeerde DPP-opvragingen per productpaspoort (
dpp_scan_aggregates, ook zonder IP-hashes) - Gebruiksstatistieken als aggregaten (
dashboard_activity_aggregates,api_operation_aggregates): actieve dagen en aantal actieve gebruikers per dashboardkanaal, verzoektellers per operatie en uitkomst — zonder gebruikers- en key-ID’s - Leveringspogingen van webhooks per webhook en gebeurtenistype (
webhook_delivery_aggregates), zonder payload en antwoord - De persoon achter het verzoek (
account), de organisatie (organization; factuur- en meldingsadres alleen voor organisatiebeheerders) en de workspace (workspace) - Productpaspoorten, metadata van DPP-documenten en bestanden (niet de bestanden zelf), GS1-identificatoren, aanvragen om ze te verifiëren en importruns (
dpp_passports,dpp_documents,files,gs1_identifiers,gs1_verification_requests,dpp_import_jobs) - Opmerkingen zonder auteursvelden (
code_comments), auditvermeldingen als aggregaten (audit_log_aggregates) en leden per rol (member_aggregates) - API-keys zonder ID en hash (
api_keys), webhooks zonder URL en geheim (webhooks) en de quotameldingen van de organisatie (scan_quota_alerts) - Tijdstempels van aanmaak en wijziging
Publishable Keys (qr3_pk_) krijgen 403: het bestand bevat de doeladressen en notities van alle codes.
Account verwijderen (Art. 17 AVG — recht om vergeten te worden)
DELETE /v1/account — Verwijdert het account van de persoon achter het verzoek, onmiddellijk en onherroepelijk: in het dashboard de ingelogde persoon, anders de persoon die de key heeft aangemaakt. Een verwijderverzoek per e-mail leidt tot dezelfde verwijdering: de beheerder verwijdert de login bij Clerk, waarna qr3 het account verwijdert.
curl -X DELETE https://qr3.app/v1/account \ -H "Authorization: Bearer qr3_sk_..."Wat er gebeurt:
- Organisaties die van de persoon zijn worden verwijderd met alle workspaces, codes, productpaspoorten, bestanden, statistieken en logs, inclusief hun bestanden in de opslag. Heeft zo’n organisatie een andere actieve organisatiebeheerder, dan neemt in plaats daarvan degene met de langste staat van dienst haar over, met de gegevens en het abonnement.
- Abonnement: een lopend abonnement eindigt onmiddellijk, zonder restitutie naar rato en zonder eindfactuur; daarna wordt de Stripe-klant verwijderd. Facturen blijven gedurende de wettelijke bewaartermijn bij Stripe (7 jaar, § 132 BAO).
- Teams: de lidmaatschappen van de persoon bij andere organisaties eindigen; die teams behouden hun gegevens. De opmerkingen van de persoon noemen geen auteur meer, de auditvermeldingen verliezen IP-pseudoniem en user agent, en de API-keys die de persoon daar heeft aangemaakt, worden ingetrokken.
- De persoon: de login (Clerk) en het gebruikersrecord worden verwijderd.
Het antwoord is 200 met status: "completed" en de tellers organizations_deleted, organizations_transferred, memberships_removed en login_deleted. Bij zeer grote organisaties is één verzoek niet genoeg: het antwoord is dan 202 met status: "in_progress", en de verwijdering rondt zich binnen enkele uren vanzelf af.
Vereist een Secret Key of Test Key met schrijfrechten, of een dashboardsessie (elke rol, omdat het om het eigen account gaat). Publishable Keys en alleen-lezen-keys krijgen 403, een key zonder persoon erachter (created_by = 'system') krijgt 409. 503 betekent dat de deployment een geheim mist dat de verwijdering nodig heeft; er is niets verwijderd.
Toestemmingen beheren
GET /v1/account/privacy/consents — Actuele toestemmingen ophalen.
POST /v1/account/privacy/consents — Toestemmingen bijwerken.
# Huidige toestemmingen ophalencurl https://qr3.app/v1/account/privacy/consents \ -H "Authorization: Bearer qr3_sk_..."
# Marketing-e-mails uitschakelencurl -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 }'Toestemmingsvelden:
| Veld | Standaard | Beschrijving |
|---|---|---|
marketing_emails | false | Nieuwsbrieven en marketing-e-mails |
analytics | true | Geaggregeerde gebruiksstatistieken |
product_updates | true | Productupdates en changelogs |
Toestemmingen wijzigen vereist schrijfrechten: de rol viewer en Publishable Keys krijgen 403.
Technische AVG-implementatie
IP-pseudonimisering (Art. 25 AVG)
HTTP-verzoek → Cloudflare Edge Worker ↓CF-Connecting-IP-header → HMAC-SHA-256(IP_HASH_SALT, doel, UTC-dag, IP) ↓ip_hash (dagelijks pseudoniem) → D1-database ↓Het oorspronkelijke IP-adres wordt nooit opgeslagenIP_HASH_SALT is een langlevend hoofdgeheim; doel en UTC-dag leiden automatisch afzonderlijke dagelijkse pseudoniemen af. Pseudoniemen van verschillende UTC-dagen zijn niet rechtstreeks gelijk, maar de houder van het geheim kan een bekende IP-kandidaat voor een bepaalde dag opnieuw berekenen. Er wordt geen KV-dagzout of handmatige dagelijkse rotatie gebruikt.
Gegevensretentie (geautomatiseerd)
| Abonnement | Retentie |
|---|---|
| Alle abonnementen | 90 dagen |
Ook gebruiksstatistieken (dashboardactiviteit, API-gebruikstellers) en webhook-leveringslogs worden 90 UTC-dagen bewaard: de lopende UTC-dag plus de 89 voorgaande dagen. Dezelfde dagelijkse cron-taak verwijdert oudere rijen (purgeOldDashboardActivity, purgeOldApiOperationUsage, purgeOldWebhookDeliveries).
Subverwerker-DPA
Cloudflare, Inc. verwerkt gegevens als verwerker. De DPA is beschikbaar op cloudflare.com/cloudflare-customer-dpa.
Cloudflare verwerkt gegevens met een locatie-indicatie die naar de EU-regio (o.a. Frankfurt) verwijst; een contractueel gegarandeerde EU-jurisdictie is momenteel niet geboekt. Omdat Cloudflare, Inc. in de VS is gevestigd, gelden Standard Contractual Clauses (SCC’s) conform Art. 46 AVG als basis voor de doorgifte.
Contact
- Functionaris voor gegevensbescherming: [email protected]
- Verwerkersovereenkomst: [email protected] (op aanvraag)
- Privacyverklaring: qr3.app/de/legal/datenschutz