API di aggregazione
GET /v1/codes/:id/scans — Serie temporali + Top paesi/dispositivi/OS per un singolo oggetto codice.
Ogni scansione di un codice QR dinamico genera un record di scansione (Scan-Record) nel tuo workspace. Il rilevamento avviene interamente sull’edge di Cloudflare — senza servizi di tracciamento esterni, senza cookie, senza che l’IP originale raggiunga mai il tuo database.
Tre modi per consumare i dati di scansione:
API di aggregazione
GET /v1/codes/:id/scans — Serie temporali + Top paesi/dispositivi/OS per un singolo oggetto codice.
Webhook (in tempo reale)
Evento qr.scanned per scansione, firmato con HMAC-SHA256. Vedi Webhook.
Dashboard
Visualizzazione nella dashboard di qr3.app — nessun richiamo API necessario.
Per ogni scansione viene creato un record nella tabella scans:
| Campo | Origine | Esempio |
|---|---|---|
id | UUID generato dal server | scn_d1f8… |
code_id | ID del codice QR scansionato | qr_a1b2c3d4 |
workspace_id | Workspace del codice | ws_xxx |
country | Cloudflare cf.country | AT |
region | Cloudflare cf.region | Vienna |
city | Cloudflare cf.city | Wien |
device_type | Parsing dello User-Agent | mobile, tablet, desktop |
os | Parsing dello User-Agent | iOS, Android, macOS, Windows |
browser | Parsing dello User-Agent | Safari, Chrome, Firefox |
referer | Header HTTP Referer | https://example.com/landing |
language | Header Accept-Language (prima lingua) | de |
redirected_to | Destinazione effettiva del reindirizzamento | https://example.com |
ip_hash | SHA-256(IP + salt giornaliero) — non reversibile | 8f3a… |
scanned_at | Timestamp ISO-8601 | 2026-05-12T14:32:11.000Z |
Tutti i dati geografici (country, region, city) provengono dall’oggetto cf di Cloudflare e si basano sul database Geo-IP di Cloudflare. Non vengono richiesti servizi Geo-IP esterni — la risoluzione avviene nello stesso Worker che esegue anche il reindirizzamento.
Questo comporta tre conseguenze:
city non è sempre disponibile (ad es. con VPN, operatori di telefonia mobile o piccole regioni). Aspettati valori null e tienine conto nelle tue dashboard.GET /v1/codes/:id/scansFornisce analytics aggregate per un singolo codice QR in un periodo di tempo selezionabile.
curl "https://qr3.app/v1/codes/qr_a1b2c3d4/scans?days=30" \ -H "Authorization: Bearer qr3_sk_..."const analytics = await qr3.scans.get('qr_a1b2c3d4', { days: 30 });console.log(analytics.period_scans, analytics.top_countries);qr3 scans qr_a1b2c3d4 --days 30Parametri di query:
| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
days | integer | 30 | Periodo in giorni (1–365) |
Risposta (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" }}Campi di risposta:
| Campo | Descrizione |
|---|---|
total_scans | Totale complessivo delle scansioni del codice (indipendente dalla finestra di giorni days) |
period_scans | Somma delle scansioni nel periodo di tempo selezionato |
scans_by_day | Bucket giornalieri, in ordine crescente per data, i giorni vuoti vengono omessi |
top_countries | Top 8 paesi nel periodo di tempo, in ordine decrescente |
top_devices | Top 5 tipi di dispositivi (mobile, tablet, desktop) |
top_os | Top 5 sistemi operativi |
I totali a livello di workspace (ad es. scansioni al mese su tutti i codici) sono disponibili tramite GET /v1/workspaces/:id nel campo scans_this_month. Per analisi incrociate tra più codici, consigliamo la dashboard o un’esportazione periodica.
Se desideri elaborare gli eventi di scansione non appena si verificano — ad esempio per dashboard in tempo reale, tracciamento dei lead o integrazioni CRM — iscriviti all’evento qr.scanned:
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" }}Documentazione completa su verifica delle firme, logica di riprovo (retry) e log di consegna in API → Webhook.
I record di scansione non contengono indirizzi IP personali. L’IP originale viene sostituito nell’Edge Worker da una funzione di hash crittografica (SHA-256) con un salt a rotazione giornaliera. Ciò significa:
La retention (conservazione dei dati) dipende dal piano:
| Piano | Conservazione |
|---|---|
| Free | 7 giorni |
| Pro | 90 giorni |
| Business / Agency | 1 anno |
| Enterprise | Personalizzato (SLA) |
Un cron job giornaliero (purgeOldScans) rimuove automaticamente i record più vecchi — il valore total_scans sull’oggetto codice rimane invariato.
days=365), il payload può raggiungere diversi KB. Applica il caching lato client con un TTL breve (ad es. 60 s).top_countries, top_devices, top_os sono limitati lato server a 8 o 5 voci. Per analisi più approfondite, esporta i dati grezzi.null: country, region, city, referer, language possono essere null. Gestisci questi casi nella tua analisi come “Sconosciuto”.