Przejdź do głównej zawartości

Śledzenie skanowań i analityka

Übersicht

Każde skanowanie dynamicznego kodu QR generuje rekord skanowania (Scan-Record) w Twoim Workspace. Rejestracja odbywa się w całości na Cloudflare-Edge — bez zewnętrznych usług śledzących, bez plików cookie i bez sytuacji, w której oryginalny adres IP kiedykolwiek trafia do Twojej bazy danych.

Trzy sposoby na konsumowanie danych skanowania:

Aggregations-API

GET /v1/codes/:id/scans — Zeitreihen + Top-Länder/-Geräte/-OS für ein einzelnes Code-Objekt.

Webhooks (Echtzeit)

qr.scanned-Event pro Scan, HMAC-SHA256-signiert. Siehe Webhooks.

Dashboard

Visualisierung im qr3.app-Dashboard — kein API-Call nötig.


Welche Daten werden erfasst

Dla każdego skanowania tworzony jest rekord w tabeli scans:

PoleŹródłoPrzykład
idUUID wygenerowane przez serwerscn_d1f8…
code_idID skanowanego kodu QRqr_a1b2c3d4
workspace_idWorkspace koduws_xxx
countryCloudflare cf.countryAT
regionCloudflare cf.regionVienna
cityCloudflare cf.cityWien
device_typeParsowanie User-Agentmobile, tablet, desktop
osParsowanie User-AgentiOS, Android, macOS, Windows
browserParsowanie User-AgentSafari, Chrome, Firefox
refererNagłówek HTTP Refererhttps://example.com/landing
languageNagłówek Accept-Language (pierwszy język)de
redirected_toRzeczywisty cel przekierowaniahttps://example.com
ip_hashSHA-256(IP + codzienny salt) — nieodwracalny8f3a…
scanned_atZnacznik czasu ISO-86012026-05-12T14:32:11.000Z

Geolokalizacja na Edge

Wszystkie dane geograficzne (country, region, city) pochodzą z obiektu cf od Cloudflare i bazują na bazie danych Geo-IP Cloudflare. Nie są odpytywane żadne zewnętrzne usługi Geo-IP — rozpoznawanie odbywa się w tym samym Workerze, który wykonuje przekierowanie.

Ma to trzy konsekwencje:

  1. Niskie opóźnienie — brak dodatkowego przeskoku (hop), brak zapytania DNS do zewnętrznego dostawcy.
  2. Ochrona danych — adres IP nie opuszcza Cloudflare i nigdy nie jest przekazywany podmiotom trzecim.
  3. Ograniczona szczegółowośćcity nie zawsze jest dostępne (np. w przypadku sieci VPN, operatorów komórkowych lub małych regionów). Należy spodziewać się wartości null i uwzględnić je w swoich dashboardach.

Odpytywanie o analitykę skanowań

GET /v1/codes/:id/scans

Zwraca zagregowane dane analityczne dla pojedynczego kodu QR w wybranym okresie.

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

Parametry zapytania (Query-Parameter):

ParametrTypDomyślnieOpis
daysinteger30Okres w dniach (1–365)

Odpowiedź (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" }
}

Pola odpowiedzi:

PoleOpis
total_scansCałkowita liczba skanowań kodu od początku jego istnienia (niezależnie od okna days)
period_scansSuma skanowań w wybranym oknie czasowym
scans_by_dayDzienne grupy (buckets), rosnąco według daty, puste dni są pomijane
top_countriesTop 8 krajów w oknie czasowym, malejąco
top_devicesTop 5 typów urządzeń (mobile, tablet, desktop)
top_osTop 5 systemów operacyjnych

Agregacja dla wielu kodów

Sumy dla całego Workspace (np. skanowania na miesiąc dla wszystkich kodów) są dostępne przez GET /v1/workspaces/:id w polu scans_this_month. Do analiz między wieloma kodami zalecamy Dashboard lub okresowy eksport.


Konsumpcja w czasie rzeczywistym przez webhooki

Jeśli chcesz przetwarzać zdarzenia skanowania natychmiast, gdy się pojawią — na przykład na potrzeby dashboardów na żywo, śledzenia leadów lub integracji z CRM — zasubskrybuj zdarzenie qr.scanned:

Okno terminala
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 (ładunek danych):

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

Pełna dokumentacja dotycząca weryfikacji sygnatur, logiki ponownych prób (retry) i logów dostarczeń znajduje się w sekcji API → Webhooks.


RODO i ochrona danych

Rekordy skanowania nie zawierają osobowych adresów IP. Oryginalny adres IP jest zastępowany w Edge-Workerze przez kryptograficzną funkcję skrótu (SHA-256) z codziennie rotowanym soleniem (salt). Oznacza to:

  • Brak możliwości ponownej identyfikacji, nawet przy znajomości algorytmu
  • Brak korelacji tego samego odwiedzającego między różnymi dniami
  • Oryginalny adres IP nigdy nie trafia do bazy danych D1

Okres przechowywania danych (retencja) zależy od planu:

PlanPrzechowywanie
Free7 dni
Pro90 dni
Business / Agency1 rok
EnterpriseNiestandardowy (SLA)

Codziennie uruchamiane zadanie cron (purgeOldScans) automatycznie usuwa starsze rekordy — pole total_scans w obiekcie kodu pozostaje nienaruszone.


Najlepsze praktyki

  • Częstotliwość odpytywania (polling): API agregacji jest wydajne, ale nie zostało zaprojektowane do odpytywania co sekundę. Do aktualizacji na żywo zawsze preferuj webhooki, a API używaj tylko do uzupełniania danych (backfill) lub dashboardów.
  • Buforowanie (caching): Odpowiedź zawiera szeregi czasowe — przy dużych oknach (days=365) ładunek danych (payload) może mieć rozmiar kilku KB. Buforuj dane po stronie klienta z krótkim czasem życia (TTL, np. 60 s).
  • Limity Top-N: top_countries, top_devices, top_os są ograniczone po stronie serwera odpowiednio do 8 lub 5 wpisów. Do głębszych analiz wyeksportuj surowe dane.
  • Tolerowanie wartości null: country, region, city, referer, language mogą mieć wartość null. W swoich analizach traktuj je jako „Nieznane”.