Incorpora la verifica dei badge nel tuo sito (senza iframe, senza SDK)
Aggiungi al tuo sito un piccolo snippet JavaScript (o una chiamata lato server) che verifica qualsiasi Open Badge tramite un endpoint JSON-LD pubblico. Niente iframe, niente tracker di terze parti.
Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure.
Se emetti credenziali, prima o poi qualcuno ti chiederà: «come faccio a sapere che questo badge è autentico?». La risposta onesta non dovrebbe mai essere «fidati del PNG». Open Badge v2.0 risolve il problema con un endpoint di verifica pubblico e leggibile dalle macchine, e mettere un widget «Verificato» sul tuo sito richiede circa dieci righe di codice: niente iframe, niente script di embed di terze parti, niente SDK.
Questo articolo mostra esattamente come chiamare l’endpoint e mostrare il risultato, in JavaScript puro e lato server con Node e Python. Spiega anche cosa controlla davvero la «verifica», dove si colloca l’endpoint rispetto alle pagine per persone che badges.ninja offre già ai destinatari, e gli errori che fanno sembrare finito un widget di verifica mentre in realtà non dimostra nulla.
Cosa significa davvero «verificato»
Un’assertion Open Badge v2.0 è un documento JSON-LD con una struttura fissa: l’assertion stessa (chi l’ha ricevuta, quando), un riferimento alla BadgeClass (di cosa si tratta, chi definisce i criteri) e un riferimento al profilo dell’emittente (chi ne garantisce la validità). Quando «verifichi» un badge, in realtà controlli tre cose distinte:
- L’assertion esiste e si risolve. L’ID del premio è reale, non inventato né manomesso.
- È stata revocata? Un emittente può revocare un badge in un secondo momento — una credenziale emessa per errore, una violazione delle regole, una certificazione scaduta — e un verificatore corretto deve segnalarlo, non limitarsi a confermare che il record esiste.
- La catena regge. I riferimenti dell’assertion al badge e all’emittente si risolvono in record reali e coerenti tra loro, non in ID orfani o discordanti.
Un widget di verifica che si limita a interrogare un URL e a controllare uno stato 200 ignora del tutto il punto 2. Un’assertion revocata si risolve comunque senza problemi: porta solo un flag di revoca nel payload. Se il tuo widget controlla soltanto «la richiesta è riuscita?», una credenziale revocata verrà mostrata come verificata, il che è peggio di non avere alcun widget.
La pagina pubblica per le persone e l’endpoint API
Ogni premio emesso su https://badges.ninja ha un URL pubblico che i destinatari possono condividere direttamente, senza bisogno di registrarsi per vederlo:

È la pagina che la maggior parte dei destinatari linkerà da LinkedIn, da un CV o dalla firma email: è pensata per le persone, mostra l’immagine del badge, l’emittente e la data di emissione, e per funzionare non richiede nulla da parte tua. L’endpoint API qui sotto restituisce la stessa assertion di base come JSON-LD strutturato, ed è ciò che ti serve quando costruisci una tua interfaccia invece di rimandare semplicemente alla nostra.
L’endpoint
GET https://api.badges.ninja/certify-badge/award/{guid}
Nessun header di autenticazione richiesto: è pubblico di proposito, perché il senso stesso degli Open Badge è che chiunque (un datore di lavoro, un selezionatore, un albo professionale) possa verificare una credenziale senza un account. I dettagli completi del contratto, compresi i nomi esatti dei campi dell’assertion restituita, sono nella documentazione sulla verifica.
Passa il GUID del premio, che avrai nei tuoi registri di emissione oppure ricavato da un link badges.ninja/awards/<guid> condiviso da un destinatario.
JS puro: fetch e rendering
Questo è l’intero widget. Nessuna fase di build, nessun pacchetto npm: lo incolli direttamente in qualsiasi pagina:
<div id="badge-verify"></div>
<script>
async function renderBadgeVerification(awardId, mountId) {
const el = document.getElementById(mountId);
el.textContent = "Checking badge status";
try {
const res = await fetch(
`https://api.badges.ninja/certify-badge/award/${awardId}`
);
if (!res.ok) {
el.textContent = "Credential not found.";
return;
}
const assertion = await res.json();
// —— revocation check first, a resolvable assertion isn't enough on its own
if (assertion.revoked) {
el.textContent = `Revoked: ${assertion.revocationReason || "no reason given"}`;
return;
}
el.textContent = `Verified, issued ${new Date(assertion.issuedOn).toLocaleDateString()}`;
} catch (err) {
el.textContent = "Verification check failed.";
}
}
renderBadgeVerification("REPLACE_WITH_AWARD_ID", "badge-verify");
</script>
Sostituisci REPLACE_WITH_AWARD_ID con un GUID di premio reale. Questa è l’intera integrazione lato client: nessun confine di iframe con cui litigare con il CSS e nessuno script di terze parti che porti con sé i propri analytics o pixel di tracciamento accanto al controllo di verifica.
Lato server: Node
Se preferisci verificare sul tuo backend — ad esempio prima di mostrare un badge «Partner verificato» su una pagina profilo che controlli tu, o prima di accettare una credenziale come prova in un flusso di onboarding — la chiamata fetch è identica, senza la parte di DOM:
async function verifyAward(awardId) {
const res = await fetch(
`https://api.badges.ninja/certify-badge/award/${awardId}`
);
if (!res.ok) return { verified: false, reason: "not_found" };
const assertion = await res.json();
if (assertion.revoked) {
return { verified: false, reason: assertion.revocationReason || "revoked" };
}
return { verified: true, issuedOn: assertion.issuedOn, badge: assertion.badge };
}
Lato server: Python
import requests
# —— resolve the assertion, then check revocation before trusting it
def verify_award(award_id):
res = requests.get(f"https://api.badges.ninja/certify-badge/award/{award_id}")
if res.status_code != 200:
return {"verified": False, "reason": "not_found"}
assertion = res.json()
if assertion.get("revoked"):
return {"verified": False, "reason": assertion.get("revocationReason", "revoked")}
return {"verified": True, "issued_on": assertion.get("issuedOn"), "badge": assertion.get("badge")}
Entrambi gli esempi trattano una risposta diversa da 200 come «non trovato» invece di lanciare un’eccezione. Un ID di premio errato o inventato è un input previsto per un endpoint di verifica pubblico, non un caso eccezionale, quindi gestiscilo come un normale ramo del flusso e non come un errore intercettato.
Dare stile al risultato invece di incorporare l’interfaccia di qualcun altro
Dato che il widget qui sopra è solo una chiamata fetch che scrive nel tuo DOM, ogni pixel è tuo. Uno schema comune prevede tre stati visivi ricavati direttamente dalla risposta:
- Verificato — un segno di spunta verde con la scritta «Verificato» e la data di emissione, nello stile dei componenti badge/tag del tuo sito.
- Revocato — uno stato attenuato o rosso che mostra il motivo della revoca invece di nasconderlo. Destinatari e terze parti traggono vantaggio da un «perché» onesto, non da un errore silenzioso.
- Non trovato — a livello visivo trattalo come «revocato» (non mostrare un finto stato di successo), ma con un testo diverso, perché un ID errato e una revoca deliberata sono situazioni diverse per chi legge la pagina.
Niente di tutto questo richiede una libreria di componenti. Basta uno switch su verified / revoked / not_found e tre classi CSS.
Cache, per non martellare l’endpoint
L’endpoint di verifica è pubblico e non autenticato, il che significa anche che non c’è alcun costo in crediti per richiesta di cui preoccuparsi — ma resta comunque una chiamata di rete a ogni caricamento di pagina se la fai lato client senza cache. Per una pagina che mostra lo stesso badge a ogni visitatore (ad esempio un badge «Partner verificato» nel footer), metti il risultato in cache lato server con un TTL breve — da cinque a quindici minuti bastano, perché le revoche non sono un segnale che cambia in fretta — invece di rifare la chiamata a ogni richiesta:
const cache = new Map();
async function verifyAwardCached(awardId, ttlMs = 10 * 60 * 1000) {
const hit = cache.get(awardId);
if (hit && Date.now() - hit.at < ttlMs) return hit.result;
const result = await verifyAward(awardId);
cache.set(awardId, { result, at: Date.now() });
return result;
}
Per un widget per singolo visitatore (un destinatario che incorpora la propria credenziale su un sito personale), salta la cache e chiama l’endpoint al momento del caricamento: la richiesta in più è trascurabile a quella scala.
Cosa offre già badges.ninja ai destinatari
Prima di costruire un embed completamente personalizzato, conviene sapere cosa è già disponibile. La pagina pubblica di ogni premio include snippet di condivisione e di embed pronti, che i destinatari possono copiare direttamente, senza scrivere codice:

Se il tuo caso d’uso è «permettere a un destinatario di mostrare che il suo badge è autentico», linkare la sua pagina badges.ninja/awards/<guid> è spesso più semplice che costruire un widget personalizzato: è la pagina per le persone vista prima e gestisce già per te la distinzione tra verificato e revocato. Costruisci il widget personalizzato di questo articolo quando ti serve lo stato di verifica mostrato dentro l’interfaccia del tuo prodotto — un elenco di partner, una dashboard di audit interno, uno strumento di selezione che controlla le certificazioni dichiarate da un candidato — dove rimandare a un altro sito non è la scelta giusta per l’esperienza utente.
Perché non un iframe o un widget di un fornitore?
Alcune piattaforme ti danno uno snippet come <iframe src="https://vendor.example.com/verify-widget"> per incorporare lo stato di verifica. Funziona, ma ha costi reali: non puoi cambiare lo stile del contenuto per adattarlo al tuo sito (ti restano i CSS forniti dal fornitore), introduce un documento cross-origin che la tua Content-Security-Policy deve consentire esplicitamente, e a ogni caricamento di pagina parte una richiesta all’host dell’iframe di una terza parte, che al visitatore interessi o meno lo stato di verifica. Un widget basato su fetch evita tutti e tre i problemi: è il tuo DOM, il tuo stile, e viene eseguito solo quando decidi di chiamarlo.
Percorrere l’intera catena di verifica
Se stai costruendo qualcosa di più completo di un widget di stato — uno strumento di audit che controlla un lotto di credenziali, per esempio, o un report di conformità — percorri l’intera catena invece di fermarti all’assertion. L’assertion rimanda a un URL badge (la BadgeClass: nome, descrizione, criteri) e la BadgeClass a sua volta rimanda a un URL issuer (il profilo dell’organizzazione). Un verificatore rigoroso risolve tutti e tre e conferma che siano coerenti tra loro — il badge appartiene davvero all’emittente che dichiara, l’assertion si riferisce davvero al badge che dichiara — e non si limita a constatare che l’assertion di primo livello risponda con un 200. È più profondità di quanta ne serva a un widget per una singola credenziale, ma è utile sapere che questa struttura esiste, se un partner o un revisore ti chiedesse mai «quanto in profondità arriva davvero la vostra verifica?».
Errori comuni da evitare
- Controllare solo lo stato HTTP. Come visto sopra, un’assertion revocata si risolve comunque. Leggi sempre
assertion.revoked, non dedurre la verifica dal solo codice di stato. - Scrivere l’ID del premio direttamente in un componente condiviso. Se il widget deve essere riutilizzabile nelle pagine di molti destinatari, ricevi l’ID del premio come parametro (un data attribute, una query string, una prop), non come valore letterale incorporato nello script.
- Ignorare in silenzio gli errori di rete. Un
try/catchche non registra nulla e mostra lo stesso testo «non trovato» di un badge davvero inesistente rende il debug penoso in seguito. Distingui «la chiamata di rete è fallita» da «l’API ha risposto di no» nei tuoi rami di errore, almeno nei log. - Riverificare a ogni render in un’app lato client. Se il widget vive dentro un componente React o Vue che si ripete spesso, verifica una volta per mount (o una volta per finestra di TTL in cache, come nella sezione sulla cache), non a ogni nuovo render.
Costa crediti? Funziona con il piano gratuito?
No. L’endpoint di verifica è deliberatamente fuori dal sistema di crediti che regola l’emissione: controllare se un badge è valido non costa nulla all’emittente, e non richiede che il visitatore che lo chiama abbia un account, una chiave API o un piano qualsiasi. Vale sia che il badge sia stato emesso con un piano Free, Starter o Pro: la verifica è una proprietà della credenziale stessa, non una funzione vincolata all’abbonamento dell’emittente. È anche il motivo per cui puoi mettere questo widget su una pagina pubblica con traffico imprevedibile (una pagina carriere, un elenco di partner) senza preoccuparti di limiti di frequenza legati al tuo account: stai chiamando un endpoint pubblico in sola lettura, non una rotta API autenticata.
Una cosa che vale la pena dire chiaramente: il JSON dell’assertion restituito dall’endpoint non include l’indirizzo email in chiaro del destinatario. L’email del destinatario è memorizzata come hash nel backend proprio perché una risposta di verifica pubblica non possa essere sfruttata per raccogliere recapiti personali: ciò che ricevi basta a confermare la credenziale (badge, emittente, date, stato di revoca) senza trasformare l’endpoint in uno strumento di lead generation per chiunque scorra gli ID dei premi.
Pronto a emettere la tua prima credenziale verificabile?
Inizia gratis su badges.ninja — designer visuale, pagina di verifica pubblica, certificato PDF, output Open Badge v2.0. Nessuna carta di credito richiesta.
Per la struttura completa e commentata di ogni campo di un’assertion, consulta il riferimento ai campi JSON-LD. E se emetti credenziali via programmazione invece di limitarti a verificarle, la guida rapida all’API copre l’autenticazione e la chiamata di creazione del premio da cui proviene l’output di questo endpoint.
Come è stato realizzato questo articolo
Alcuni articoli di questo blog vengono redatti con l'aiuto di un assistente IA e poi revisionati, verificati e modificati dal team di Badges Ninja prima della pubblicazione. Ogni esempio di codice e ogni prezzo viene verificato rispetto al prodotto live. Scopri di più sul nostro processo editoriale e sull'uso dell'IA nella nostra pagina sul processo editoriale .

Informazioni sull'autore
Nacho Coll
Founder & Engineer at Badges Ninja
Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure. Writes about the Open Badges spec, credential verification, and running a credentialing platform serverless on AWS, from the operator side of the wire.
Altri articoli di Nacho Coll
- Open Badges con Salesforce: emettere credenziali verificabili dai trigger del CRM1 ott 2026 · 10min di lettura
- Emettere Open Badges tramite l'API REST di Badges Ninja: guida rapida28 set 2026 · 10min di lettura
- Certifier vs Accredible: confronto tra piattaforme di certificati (2026)21 set 2026 · 10min di lettura

