Pular para o conteúdo

Rastreamento de Scans & Analytics

Visão Geral

Cada leitura de um QR Code dinâmico gera um registro de scan no seu Workspace. A captura ocorre inteiramente no Cloudflare Edge — sem serviços de rastreamento externos, sem cookies e sem que o IP original chegue ao seu banco de dados.

Três maneiras de consumir os dados de scan:

API de Agregação

GET /v1/codes/:id/scans — Séries temporais + principais países/dispositivos/sistemas operacionais para um único objeto de código.

Webhooks (Tempo Real)

Evento qr.scanned por scan, assinado com HMAC-SHA256. Consulte Webhooks.

Painel

Visualização no painel do qr3.app — sem necessidade de chamadas de API.


Quais dados são coletados

Para cada scan, um registro é criado na tabela scans:

CampoOrigemExemplo
idUUID gerado pelo servidorscn_d1f8…
code_idID do QR Code escaneadoqr_a1b2c3d4
workspace_idWorkspace do códigows_xxx
countryCloudflare cf.countryAT
regionCloudflare cf.regionVienna
cityCloudflare cf.cityWien
device_typeAnálise do User-Agentmobile, tablet, desktop
osAnálise do User-AgentiOS, Android, macOS, Windows
browserAnálise do User-AgentSafari, Chrome, Firefox
refererCabeçalho HTTP Refererhttps://example.com/landing
languageCabeçalho Accept-Language (primeiro idioma)de
redirected_toDestino real do redirecionamentohttps://example.com
ip_hashPseudónimo diário HMAC-SHA-256 (segredo duradouro + finalidade + dia UTC + IP)8f3a…
scanned_atTimestamp ISO-86012026-05-12T14:32:11.000Z

Geolocalização no Edge

Todos os dados geográficos (country, region, city) vêm do objeto cf da Cloudflare e são baseados no banco de dados Geo-IP da Cloudflare. Nenhum serviço externo de Geo-IP é consultado — a resolução ocorre no mesmo Worker que executa o redirecionamento.

Isso tem três consequências:

  1. Baixa latência — sem saltos adicionais, sem consulta de DNS a um provedor externo.
  2. Privacidade de dados — o endereço IP não sai da Cloudflare e nunca é compartilhado com terceiros.
  3. Granularidade limitada — a cidade (city) nem sempre está disponível (por exemplo, com VPNs, operadoras de celular ou regiões pequenas). Espere valores null e planeje seus painéis considerando isso.

Consultar Analytics de Scans

GET /v1/codes/:id/scans

Retorna analytics agregados para um único QR Code em um período selecionável.

Terminal window
curl "https://qr3.app/v1/codes/qr_a1b2c3d4/scans?days=30" \
-H "Authorization: Bearer qr3_sk_..."

Parâmetros de Query:

ParâmetroTipoPadrãoDescrição
daysinteger30Período em dias (1–365)

Resposta (HTTP 200):

{
"data": {
"code_id": "qr_a1b2c3d4",
"short_code": "r7f3Kx",
"total_scans": 1842,
"period_days": 30,
"period_scans": 367,
"scans_by_day": [
{ "date": "2026-04-13", "count": 12 },
{ "date": "2026-04-14", "count": 18 }
],
"top_countries": [
{ "value": "AT", "count": 142 },
{ "value": "DE", "count": 98 },
{ "value": "CH", "count": 41 }
],
"top_devices": [
{ "value": "mobile", "count": 281 },
{ "value": "desktop", "count": 72 },
{ "value": "tablet", "count": 14 }
],
"top_os": [
{ "value": "iOS", "count": 158 },
{ "value": "Android", "count": 123 },
{ "value": "macOS", "count": 48 }
]
},
"meta": { "request_id": "req_xyz123" }
}

Campos de Resposta:

CampoDescrição
total_scansTotal acumulado de scans do código (independente do período de days)
period_scansSoma dos scans no período selecionado
scans_by_dayAgrupamentos diários, em ordem crescente de data, dias vazios são omitidos
top_countriesPrincipais 8 países no período, em ordem decrescente
top_devicesPrincipais 5 tipos de dispositivos (mobile, tablet, desktop)
top_osPrincipais 5 sistemas operacionais

Agregação de múltiplos códigos

Totais de todo o Workspace (por exemplo, scans por mês em todos os códigos) estão disponíveis através de GET /v1/workspaces/:id no campo scans_this_month. Para análises cruzadas de códigos, recomendamos o painel ou uma exportação periódica.


Consumo em tempo real via Webhooks

Se você deseja processar eventos de scan assim que eles acontecem — por exemplo, para painéis em tempo real, rastreamento de leads ou integrações de CRM — assine o evento qr.scanned:

Terminal window
curl -X POST https://qr3.app/v1/webhooks \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/qr3",
"events": ["qr.scanned"],
"secret": "my-secret-key-min-16-chars"
}'

Payload:

{
"id": "evt_abc123xyz",
"type": "qr.scanned",
"created": "2026-05-12T14:32:11.000Z",
"data": {
"code_id": "qr_a1b2c3d4",
"short_code": "r7f3Kx",
"scan_id": "scn_d1f8...",
"country": "AT",
"device_type": "mobile",
"os": "iOS"
}
}

Documentação completa sobre verificação de assinatura, lógica de repetição e logs de entrega em API → Webhooks.


RGPD & Privacidade de Dados

Os registros não armazenam o IP original. O Edge Worker deriva um pseudónimo HMAC-SHA-256 de um segredo duradouro, finalidade e dia UTC; o pseudónimo continua a ser dado pessoal.

  • Sem o segredo o valor não é diretamente reversível, mas o titular do segredo pode recalcular candidatos IP conhecidos para um dia UTC.
  • A finalidade e o dia UTC fazem parte da derivação, pelo que hashes do mesmo scanner em dias distintos não são diretamente comparáveis.
  • O IP original não chega a D1, KV nem R2.

Em janelas de vários dias, unique_scanners é uma métrica de Dias de leitura, não de pessoas: o mesmo scanner recebe um pseudónimo separado para cada dia UTC.

A retenção depende do plano:

PlanoRetenção
Free7 dias
Pro90 dias
Business / Agency1 ano
EnterprisePersonalizado (SLA)

Uma tarefa cron diária (purgeOldScans) remove registros mais antigos automaticamente — o campo total_scans no objeto de código permanece inalterado.


Boas Práticas

  • Frequência de Polling: A API de agregação é eficiente, mas não foi projetada para consultas a cada segundo. Para atualizações em tempo real, prefira sempre Webhooks e use a API apenas para backfills / painéis.
  • Caching: A resposta contém séries temporais — com períodos grandes (days=365), o payload pode ter vários KB. Faça cache no lado do cliente com um TTL curto (por exemplo, 60 s).
  • Limites Top-N: top_countries, top_devices e top_os são limitados no lado do servidor a 8 ou 5 entradas, respectivamente. Para análises mais profundas, exporte os dados brutos.
  • Tolerar valores null: country, region, city, referer e language podem ser null. Trate isso em sua análise como “Desconhecido”.