Ga naar inhoud

Scan-Tracking & Analytics

Overzicht

Elke scan van een dynamische QR-code genereert een scan-record in je workspace. De registratie vindt volledig plaats op de Cloudflare-edge — zonder externe trackingdiensten, zonder cookies en zonder dat het originele IP-adres ooit je database bereikt.

Drie manieren om scangegevens te consumeren:

Aggregatie-API

GET /v1/codes/:id/scans — Tijdreeksen + top-landen/-apparaten/-besturingssystemen voor een afzonderlijk code-object.

Webhooks (realtime)

qr.scanned-event per scan, HMAC-SHA256-gesigneerd. Zie Webhooks.

Dashboard

Visualisatie in het qr3.app-dashboard — geen API-call nodig.


Welke gegevens worden verzameld

Per scan wordt er een record aangemaakt in de tabel scans:

VeldBronVoorbeeld
idDoor de server gegenereerde UUIDscn_d1f8…
code_idCode-ID van de gescande QR-codeqr_a1b2c3d4
workspace_idWorkspace van de codews_xxx
countryCloudflare cf.countryAT
regionCloudflare cf.regionVienna
cityCloudflare cf.cityWien
device_typeUser-Agent-parsingmobile, tablet, desktop
osUser-Agent-parsingiOS, Android, macOS, Windows
browserUser-Agent-parsingSafari, Chrome, Firefox
refererHTTP Referer-headerhttps://example.com/landing
languageAccept-Language-header (eerste taal)de
redirected_toDaadwerkelijke redirect-doelhttps://example.com
ip_hashDagelijks HMAC-SHA-256-pseudoniem (langdurig geheim + doel + UTC-dag + IP)8f3a…
scanned_atISO-8601-timestamp2026-05-12T14:32:11.000Z

Geolocatie op de edge

Alle geogegevens (country, region, city) zijn afkomstig uit het cf-object van Cloudflare en zijn gebaseerd op de Cloudflare Geo-IP-database. Er worden geen externe Geo-IP-services aangeroepen — de herleiding vindt plaats in dezelfde Worker die ook de redirect uitvoert.

Dit heeft drie gevolgen:

  1. Lage latentie — geen extra hop, geen DNS-lookup naar een externe provider.
  2. Privacy — het IP-adres verlaat Cloudflare niet en wordt nooit met derden gedeeld.
  3. Beperkte granulariteitcity is niet altijd beschikbaar (bijv. bij VPN’s, mobiele providers of kleine regio’s). Houd rekening met null-waarden in je dashboards.

Scan-analytics opvragen

GET /v1/codes/:id/scans

Levert geaggregeerde analytics voor een afzonderlijke QR-code over een te selecteren periode.

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

Queryparameters:

ParameterTypeStandaardBeschrijving
daysinteger30Periode in dagen (1–365)

Response (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" }
}

Responsevelden:

VeldBeschrijving
total_scansTotaal aantal scans van de code over de gehele levensduur (onafhankelijk van de days-periode)
period_scansTotaal aantal scans in de geselecteerde periode
scans_by_dayDagelijkse buckets, oplopend op datum, lege dagen worden weggelassen
top_countriesTop 8 landen in de periode, aflopend
top_devicesTop 5 apparaattypen (mobile, tablet, desktop)
top_osTop 5 besturingssystemen

Aggregatie over meerdere codes

Workspace-brede totalen (bijv. scans per maand over alle codes) zijn beschikbaar via GET /v1/workspaces/:id in het veld scans_this_month. Voor analyses over meerdere codes raden we het dashboard of een periodieke export aan.


Realtime consumptie via webhooks

Als je scan-events wilt verwerken zodra ze plaatsvinden — bijvoorbeeld voor live dashboards, lead-tracking of CRM-integraties — abonneer je dan op het qr.scanned-event:

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"
}
}

Volledige documentatie over handtekeningverificatie, retry-logica en delivery-logs in API → Webhooks.


AVG & privacy

Scan-records slaan het oorspronkelijke IP niet op. De edge-worker leidt een HMAC-SHA-256-pseudoniem af uit een langdurig geheim, doel en UTC-dag; het pseudoniem blijft een persoonsgegeven.

  • Zonder het geheim is de waarde niet rechtstreeks omkeerbaar, maar een geheimhouder kan bekende IP-kandidaten voor een UTC-dag opnieuw berekenen.
  • Doel en UTC-dag maken deel uit van de afleiding; hashes van dezelfde scanner op verschillende dagen zijn dus niet rechtstreeks vergelijkbaar.
  • Het oorspronkelijke IP bereikt D1, KV of R2 niet.

In vensters van meerdere dagen is unique_scanners een Scannerdagen-metriek, geen personenmetriek: dezelfde scanner krijgt voor elke UTC-dag een afzonderlijk pseudoniem.

Bewaartermijn is afhankelijk van het abonnement:

AbonnementBewaartermijn
Free7 dagen
Pro90 dagen
Business / Agency1 jaar
EnterpriseCustom (SLA)

Een dagelijkse cronjob (purgeOldScans) verwijdert automatisch oudere records — total_scans op het code-object blijft hierdoor ongewijzigd.


Best practices

  • Polling-frequentie: De aggregatie-API is efficiënt, maar niet bedoeld voor updates per seconde. Geef voor live updates altijd de voorkeur aan webhooks en gebruik de API alleen voor backfills of dashboards.
  • Caching: De response bevat tijdreeksen — bij grote periodes (days=365) kan de payload meerdere KB groot worden. Cache aan de clientzijde met een korte TTL (bijv. 60 s).
  • Top-N-limieten: top_countries, top_devices en top_os zijn aan de serverzijde beperkt tot respectievelijk 8 of 5 vermeldingen. Exporteer de ruwe gegevens voor diepgaandere analyses.
  • Houd rekening met null-waarden: country, region, city, referer en language kunnen null zijn. Behandle dit in je analyses als “Onbekend”.