L’API REST di DenQr fa tutto quello che fa la dashboard: creare QR Code statici e dinamici, cambiare la destinazione di un dinamico senza ristampare, leggere le statistiche, ricevere le scansioni in tempo reale via webhook. Base URL: https://api.denqr.com/api/v1. Tutte le risposte sono JSON.
L’API è inclusa dal piano Starter (500 richieste al mese, 60 al minuto), Pro (3.000 al mese, 120 al minuto), Business ed Enterprise (senza tetto mensile). Il contatore si azzera il primo del mese; lo vedi in Dashboard → API & Webhook.
1. Autenticazione
Chiave API
Crea una chiave da Dashboard → API & Webhook → Chiavi API. Ha il formato dq_…, si mostra una sola volta e può essere di sola lettura o di lettura e scrittura. Passala nell’header Authorization:
curl https://api.denqr.com/api/v1/qr \
-H "Authorization: Bearer dq_xxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"Una chiave lavora sull’organizzazione che l’ha creata. Le rotte di fatturazione, account e pannello staff non sono raggiungibili con una chiave.
OAuth 2.0 (app di terzi)
Se il tuo software deve agire per conto di altri utenti DenQr, registra un’app da Dashboard → API & Webhook → App OAuth e usa il flusso Authorization Code con PKCE. Scope disponibili: read e write.
1. Manda l'utente a
https://denqr.com/oauth/authorize?response_type=code
&client_id=CLIENT_ID&redirect_uri=https://tuaapp.it/callback
&scope=read%20write&state=STATO
&code_challenge=BASE64URL(SHA256(verifier))&code_challenge_method=S256
2. L'utente autorizza e torna su redirect_uri?code=…&state=…
3. Scambia il codice con i token (form-urlencoded o JSON):
POST https://api.denqr.com/api/v1/oauth/token
grant_type=authorization_code&code=…&redirect_uri=…
&client_id=…&code_verifier=… # app pubbliche (PKCE)
&client_secret=… # in alternativa, app server
→ { "access_token": "dqo_…", "token_type": "Bearer", "expires_in": 3600,
"refresh_token": "dqr_…", "scope": "read write" }
4. Rinnova con grant_type=refresh_token&refresh_token=dqr_…&client_id=…
(il refresh token vecchio viene invalidato: usa sempre l'ultimo ricevuto)
5. Revoca: POST https://api.denqr.com/api/v1/oauth/revoke token=…&client_id=…I token dqo_… si usano come una chiave API. Le richieste contano nella quota dell’organizzazione che ha autorizzato l’app.
2. QR Code
GET /qr— elenco, con search, status, type, mode, folderId, sort, page, perPagePOST /qr— crea un QR CodeGET /qr/:id— dettaglioPATCH /qr/:id— modifica nome, cartella, scadenza, password, UTM…POST /qr/:id/destination— cambia la destinazione di un dinamico (body: { payload, rules? })PATCH /qr/:id/design— cambia colori, forma, logo, cornicePOST /qr/:id/duplicate— duplicaPOST /qr/:id/archive— sposta nel cestino (i dinamici smettono di rispondere)POST /qr/:id/restore— ripristina dal cestinoDELETE /qr/:id— elimina definitivamenteGET /qr/:id/export?format=png|svg|pdf&size=1024— scarica il fileGET /qr/types— tipi disponibili con i campi del payload (pubblica)
Creare un QR
POST https://api.denqr.com/api/v1/qr
Content-Type: application/json
{
"name": "Menu tavolo 4",
"type": "url", // vedi GET /qr/types: url, text, email, wifi, vcard, menu, …
"mode": "dynamic", // "static" = contenuto dentro al codice, non modificabile
"payload": { "url": "https://esempio.it/menu" },
"folderId": null, // opzionale
"expiresAt": null, // opzionale, ISO 8601
"maxScans": null, // opzionale
"utm": { "utm_source": "tavolo" }, // opzionale
"design": { "foreground": "#0C1B29", "pattern": "rounded" } // opzionale
}
→ 200 { "qr": { "id": "…", "shortCode": "waiTH7J", "shortUrl": "https://qr.denqr.com/waiTH7J", … } }Un QR dinamico passa da qr.denqr.com: puoi cambiarne la destinazione dopo la stampa e ricevi le statistiche. Conta nella quota di QR dinamici del piano.
3. Cartelle e statistiche
GET /folders— elenco cartellePOST /folders— crea (body: { name })PATCH /folders/:id— rinominaDELETE /folders/:id— elimina (i QR restano, senza cartella)GET /analytics/overview?from&to&qrId&country&device— scansioni totali, uniche, serie giornaliera, paesi, dispositiviGET /analytics/qr/:id?from&to— statistiche di un singolo QRGET /analytics/export?from&to— CSV delle scansioni
4. Webhook
Da Dashboard → API & Webhook → Webhook (o via API su /developer/hooks) registri un URL https pubblico e scegli gli eventi. Per ognuno ricevi un POST JSON:
POST https://tuoserver.it/denqr
Content-Type: application/json
X-DenQr-Event: qr.scanned
X-DenQr-Delivery: cmu5rgifh000qlt11shx4obxl
X-DenQr-Signature: t=1789660419,v1=5f1a…
{
"id": "cmu5rgifh000plt11ofyznmy4",
"type": "qr.scanned",
"created_at": "2026-09-17T16:45:31.086Z",
"data": {
"qr_id": "…", "short_code": "waiTH7J", "scanned_at": "…", "unique": true,
"country": "IT", "city": "Milano", "device": "mobile", "os": "iOS", "browser": "Safari",
"referrer": null, "utm": null
}
}Eventi
qr.created,qr.updated,qr.destination_changed,qr.archived,qr.deletedqr.scanned— ogni scansione di un QR dinamico, senza dati personali (niente IP)
Verificare la firma
Calcola HMAC-SHA256(segreto, t + "." + corpo_grezzo) e confrontalo con v1 in modo a tempo costante; rifiuta se t è più vecchio di 5 minuti. Il segreto (whsec_…) lo vedi una sola volta alla creazione del webhook.
// Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifica(segreto, header, corpo) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const atteso = createHmac('sha256', segreto).update(`${t}.${corpo}`).digest('hex');
return timingSafeEqual(Buffer.from(atteso), Buffer.from(v1));
}Consegna e retry
Rispondi 2xx entro 10 secondi. Altrimenti riproviamo 5 volte con attese crescenti (1 min, 5 min, 30 min, 2 h, 12 h). Dopo 20 consegne fallite di fila il webhook si spegne da solo: lo riattivi dalla dashboard. Ogni evento e ogni tentativo restano visibili 30 giorni nel tab Eventi.
5. Errori e limiti
Gli errori hanno sempre la forma { "error": { "code": "…", "message": "…", "details": {} } }.
401 UNAUTHENTICATED— chiave o token mancanti, non validi, revocati o scaduti403 FORBIDDEN— scope insufficiente (servewrite) o rotta non disponibile via API403 FEATURE_NOT_AVAILABLE— funzione non inclusa nel piano422 VALIDATION_FAILED— campi mancanti o non validi (detailsspiega quali)404 QR_NOT_FOUND/NOT_FOUND429 RATE_LIMITED— troppe richieste al minuto403 QUOTA_EXCEEDED— quota mensile o limite del piano raggiunto
Domande o bisogno di più richieste? Scrivi a supporto@denqr.com.