Pular para o conteúdo

RGPD & Proteção de Dados

Privacidade por Design

O qr3.app foi desenvolvido desde o início em conformidade com o RGPD:

  • Pseudonimização de IP — Os endereços IP são imediatamente convertidos em pseudônimos HMAC-SHA-256 do dia UTC no Edge Worker. O IP original nunca é armazenado; o pseudônimo continua a ser um dado pessoal.
  • Minimização de dados — Apenas os dados estritamente necessários para o funcionamento são coletados.

Privacy-API

Resumo dos dados (Art. 15.º do RGPD)

GET /v1/account/privacy — Retorna uma visão geral de todos os dados armazenados.

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

Response:

{
"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 conta os registros armazenados. scan_records são leituras de códigos QR, e dpp_scan_records, consultas a passaportes de produto pelo resolver DPP público. Ambos contêm o pseudônimo de IP.

dashboard_activity_records e dashboard_document_activity_records contam os dias em que um membro abriu o dashboard (duas tabelas), api_operation_records os contadores diários de requisições por chave de API, operação e resultado, e webhook_delivery_records as tentativas de entrega de webhooks, inclusive as de webhooks excluídos. Nenhum deles armazena endereço IP ou User-Agent; o log de entregas armazena, porém, o payload do evento e o primeiro quilobyte (1 KB) da resposta do destinatário. Os quatro são eliminados após 90 dias UTC (usage_retention, webhook_delivery_retention).

account indica a pessoa por trás da solicitação — no dashboard, a pessoa conectada; caso contrário, a pessoa que criou a chave — com e-mail, nome, idioma e data de criação; para uma Publishable Key o valor é null. Os demais contadores cobrem todas as outras tabelas com dados da conta: passaportes de produto, documentos DPP, arquivos, identificadores GS1, comentários, registros de auditoria, execuções de importação, membros do workspace e os avisos de cota da organização (scan_quota_alerts). Linhas excluídas logicamente contam, porque ainda estão armazenadas. erasure, backup_retention e billing_records descrevem o que DELETE /v1/account exclui e o que permanece por um tempo; pii_in_export é true porque a exportação indica a pessoa (e-mail, nome) — os pseudônimos de IP nunca saem do banco de dados.


Exportação de dados (Art. 20.º do RGPD — Portabilidade dos dados)

GET /v1/account/export — Baixa todos os dados como um arquivo JSON.

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

A exportação contém:

  • Todos os códigos QR (incluindo os eliminados)
  • Estatísticas de leitura agregadas (sem hashes de IP originais)
  • Consultas DPP agregadas por passaporte de produto (dpp_scan_aggregates, também sem hashes de IP)
  • Estatísticas de uso agregadas (dashboard_activity_aggregates, api_operation_aggregates): dias ativos e número de usuários ativos por canal do dashboard, contadores de requisições por operação e resultado, sem IDs de usuários nem de chaves
  • Tentativas de entrega de webhooks por webhook e tipo de evento (webhook_delivery_aggregates), sem payload nem resposta
  • A pessoa por trás da solicitação (account), a organização (organization; endereço de cobrança e de notificação apenas para administradores da organização) e o workspace (workspace)
  • Passaportes de produto, metadados de documentos DPP e de arquivos (não os arquivos em si), identificadores GS1, solicitações de verificação deles e execuções de importação (dpp_passports, dpp_documents, files, gs1_identifiers, gs1_verification_requests, dpp_import_jobs)
  • Comentários sem campos de autor (code_comments), registros de auditoria como agregados (audit_log_aggregates) e membros por função (member_aggregates)
  • Chaves de API sem ID e hash (api_keys), webhooks sem URL e segredo (webhooks) e os avisos de cota da organização (scan_quota_alerts)
  • Carimbos de data/hora de criação e alteração

As Publishable Keys (qr3_pk_) recebem 403: o arquivo contém os endereços de destino e as notas de todos os códigos.


Eliminar conta (Art. 17.º do RGPD — Direito ao esquecimento)

DELETE /v1/account — Exclui a conta da pessoa por trás da solicitação, imediatamente e de forma irreversível: no dashboard, a pessoa conectada; caso contrário, a pessoa que criou a chave. Um pedido de exclusão por e-mail leva à mesma exclusão: o operador exclui o login no Clerk e, em seguida, o qr3 exclui a conta.

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

O que acontece:

  1. As organizações que pertencem à pessoa são excluídas com todos os workspaces, códigos, passaportes de produto, arquivos, estatísticas e logs, incluindo seus arquivos no armazenamento. Se uma dessas organizações tiver outro administrador da organização ativo, quem a assume em vez disso é o mais antigo — com os dados e a assinatura.
  2. Assinatura: uma assinatura em andamento termina imediatamente, sem reembolso proporcional e sem fatura final; em seguida, o cliente no Stripe é excluído. As faturas permanecem no Stripe durante o prazo legal de guarda (7 anos, § 132 BAO).
  3. Equipes: as associações da pessoa a outras organizações terminam; essas equipes mantêm seus dados. Os comentários da pessoa deixam de indicar um autor, os registros de auditoria dela perdem o pseudônimo de IP e o user agent, e as chaves de API que ela criou lá são revogadas.
  4. A pessoa: o login (Clerk) e o registro de usuário são excluídos.

A resposta é 200 com status: "completed" e os contadores organizations_deleted, organizations_transferred, memberships_removed e login_deleted. Em organizações muito grandes, uma solicitação não basta: a resposta é então 202 com status: "in_progress", e a exclusão é concluída automaticamente em poucas horas.

Exige uma Secret Key ou Test Key com permissão de escrita, ou uma sessão no dashboard (qualquer função, pois se trata da própria conta). Publishable Keys e chaves somente leitura recebem 403, uma chave sem pessoa por trás (created_by = 'system') recebe 409. 503 significa que falta à implantação um segredo de que a exclusão precisa; nada foi excluído.


Gerenciar consentimentos

GET /v1/account/privacy/consents — Obter os consentimentos atuais.

POST /v1/account/privacy/consents — Atualizar os consentimentos.

Terminal window
# Obter os consentimentos atuais
curl https://qr3.app/v1/account/privacy/consents \
-H "Authorization: Bearer qr3_sk_..."
# Desativar os e-mails de marketing
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 }'

Campos de consentimento:

CampoPadrãoDescrição
marketing_emailsfalseNewsletter e e-mails promocionais
analyticstrueEstatísticas de utilização agregadas
product_updatestrueNovidades do produto e registros de alterações (changelogs)

Alterar os consentimentos exige permissões de escrita: a função viewer e as Publishable Keys recebem 403.


Implementação técnica do RGPD

Pseudonimização de IP (Art. 25.º do RGPD)

Requisição HTTP → Cloudflare Edge Worker
↓
Cabeçalho CF-Connecting-IP → HMAC-SHA-256(IP_HASH_SALT, finalidade, dia UTC, IP)
↓
ip_hash (pseudônimo diário) → banco de dados D1
↓
O IP original nunca é armazenado

IP_HASH_SALT é um segredo mestre de longa duração; a finalidade e o dia UTC derivam automaticamente pseudônimos diários distintos. Os pseudônimos de dias UTC diferentes não são diretamente iguais, mas o detentor do segredo pode recalcular um candidato de IP conhecido para um dia específico. Não é usado sal diário em KV nem rotação manual diária.

Retenção de dados (automatizada)

PlanoRetenção
Todos os planos90 dias

As estatísticas de uso (atividade do dashboard, contadores de uso da API) e os logs de entrega de webhooks também são mantidos por 90 dias UTC: o dia UTC atual e os 89 anteriores. A mesma tarefa cron diária elimina as linhas mais antigas (purgeOldDashboardActivity, purgeOldApiOperationUsage, purgeOldWebhookDeliveries).

DPA de Subprocessadores

A Cloudflare, Inc. processa dados como subcontratante. O DPA está disponível em cloudflare.com/cloudflare-customer-dpa.

A Cloudflare processa dados com uma indicação de localização para a região da UE (entre outros, Frankfurt); uma jurisdição da UE contratualmente garantida não está atualmente contratada. Como a Cloudflare, Inc. está sediada nos EUA, aplicam-se as Cláusulas Contratuais-Tipo (SCCs) nos termos do Art. 46.º do RGPD como base de transferência.


Contato