Open Badges con Salesforce: emettere credenziali verificabili dai trigger del CRM

Fai partire l'emissione di Open Badge da qualsiasi evento di un oggetto Salesforce — Lead vinto, Account promosso, traguardo formativo — con una callout Apex verso l'API di Badges Ninja.

Nacho Coll Di Aggiornato 10 min di lettura
Fai partire l'emissione di Open Badge da qualsiasi evento di un oggetto Salesforce — Lead vinto, Account promosso, traguardo formativo — con una callout Apex verso l'API di Badges Ninja.

La maggior parte dei team di formazione registra già in Salesforce chi ha completato cosa. L’iscrizione a un corso passa a «Completed», un account partner raggiunge un livello di certificazione, un cliente termina l’onboarding. Il record cambia, e poi qualcuno esporta un elenco e invia i certificati a mano una settimana dopo.

Questa guida mostra come colmare quel vuoto: quando un record raggiunge lo stato che ti interessa, Salesforce chiama l’API di https://badges.ninja, viene emessa una credenziale Open Badge v2.0 e il link di verifica viene salvato di nuovo sul record. A quel punto puoi inserire il link in un template email, in un report o in una pagina di Experience Cloud.

Ti serviranno un amministratore Salesforce (o una developer sandbox), un badge già progettato su https://badges.ninja e una chiave API. Se non hai mai chiamato l’API, leggi prima la guida rapida all’API: bastano dieci minuti.

Quando un trigger del CRM è lo strumento giusto

Prima di scrivere Apex, verifica se ti serve davvero. Se emetti le credenziali in blocco una volta a semestre o a trimestre, il caricamento CSV della dashboard è più veloce e non richiede alcun codice. La strada Salesforce si giustifica quando:

  • L’emissione deve avvenire nel momento in cui un record cambia, senza alcun passaggio manuale.
  • I dati su «chi l’ha ottenuto» risiedono già in Salesforce e non vuoi una seconda fonte di verità.
  • Vuoi salvare il link della credenziale su Contact, Lead o su un record di formazione personalizzato, per i report.

Se il tuo strumento di workflow è Zapier e non Apex, la stessa chiamata API funziona anche lì; vedi l’automazione Open Badges con Zapier.

Cosa stai costruendo

Il flusso ha quattro elementi:

  1. Un badge progettato nel designer visuale. Copia il suo id (l’id restituito da GET /badges).
  2. Una chiave API creata nella pagina API Keys della tua dashboard. La chiave viene mostrata una sola volta, quindi salvala subito.
  3. Una Named Credential in Salesforce che custodisce la chiave, così non compare mai nel codice.
  4. Un trigger Apex più una classe Queueable che emette il riconoscimento e scrive il link dell’award sul record.

Newly created API key revealed once

Passo 1: salva la chiave in una Named Credential

Inserire una chiave API direttamente in Apex è l’errore da evitare. In Setup crea una External Credential con un header personalizzato, poi una Named Credential che punti all’host dell’API:

  • URL: https://api.badges.ninja
  • Nome dell’header personalizzato: X-Api-Key
  • Valore dell’header personalizzato: la tua chiave, nella forma bws_YOUR_KEY_HERE
  • Nome della Named Credential: BadgesNinja

Concedi al permission set che esegue la tua automazione l’accesso al principal della External Credential. Con una Named Credential non serve una Remote Site Setting separata; Salesforce autorizza la callout tramite la credenziale. Se la tua org usa ancora le Named Credential legacy o un endpoint semplice, aggiungi https://api.badges.ninja come Remote Site Setting e invia l’header dal codice.

Passo 2: aggiungi i campi al record

Sull’oggetto che rappresenta il traguardo (useremo un oggetto personalizzato chiamato Training_Enrollment__c), aggiungi:

  • Badge_Id__c (Text): il badge da emettere. Salvarlo per ogni corso permette a un solo trigger di servire molti badge.
  • Badge_Award_Id__c (Text, unique): l’id dell’award restituito dall’API.
  • Badge_Url__c (URL): la pagina pubblica di verifica.
  • Badge_Error__c (Long Text): l’ultimo errore, così gli amministratori vedono i fallimenti senza aprire i debug log.

Il campo Badge_Award_Id__c ha una doppia funzione. È la tua protezione dai duplicati: il trigger emette solo quando è vuoto.

Passo 3: la callout Apex

Un trigger non può eseguire una callout direttamente, perché le callout non sono consentite nella stessa transazione di un DML non ancora confermato. Affida il lavoro a un Queueable che implementa Database.AllowsCallouts.

Il contratto API di POST /awards è breve. Il body contiene badgeId, un oggetto annidato recipient con name ed email, e un issuedOn obbligatorio. Una chiamata riuscita restituisce HTTP 200 con { "awardId": "..." }. Il nome del destinatario deve avere almeno cinque caratteri, quindi un Contact chiamato «Al Li» verrebbe rifiutato; valida i dati prima di chiamare.

public class BadgeIssueJob implements Queueable, Database.AllowsCallouts {
    private Id enrollmentId;
    public BadgeIssueJob(Id enrollmentId) { this.enrollmentId = enrollmentId; }

    public void execute(QueueableContext ctx) {
        Training_Enrollment__c e = [
            SELECT Id, Badge_Id__c, Badge_Award_Id__c, Contact__r.Name, Contact__r.Email
            FROM Training_Enrollment__c WHERE Id = :enrollmentId LIMIT 1
        ];
        if (e.Badge_Award_Id__c != null) { return; }   // already issued

        Map<String, Object> recipient = new Map<String, Object>{
            'name' => e.Contact__r.Name,
            'email' => e.Contact__r.Email
        };
        Map<String, Object> body = new Map<String, Object>{
            'badgeId' => e.Badge_Id__c,
            'recipient' => recipient,
            'issuedOn' => Datetime.now().getTime()
        };

        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:BadgesNinja/awards');
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setBody(JSON.serialize(body));

        HttpResponse res = new Http().send(req);
        if (res.getStatusCode() == 200) {
            Map<String, Object> out = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
            String awardId = (String) out.get('awardId');
            e.Badge_Award_Id__c = awardId;
            e.Badge_Url__c = 'https://badges.ninja/awards/' + awardId;
            e.Badge_Error__c = null;
        } else {
            e.Badge_Error__c = res.getStatusCode() + ': ' + res.getBody();
        }
        update e;
    }
}

Due dettagli da notare. Primo, issuedOn è in millisecondi epoch, esattamente ciò che restituisce Datetime.getTime(). Secondo, la risposta non include l’URL di verifica, quindi il codice lo costruisce a partire dall’id dell’award: https://badges.ninja/awards/<awardId>.

Passo 4: il trigger

Tieni il trigger snello. Deve solo decidere quali record sono idonei e mettere in coda un job per ciascuno.

trigger TrainingEnrollmentBadge on Training_Enrollment__c (after insert, after update) {
    for (Training_Enrollment__c e : Trigger.new) {
        Boolean completed = e.Status__c == 'Completed';
        Boolean wasCompleted = Trigger.isUpdate && Trigger.oldMap.get(e.Id).Status__c == 'Completed';
        if (completed && !wasCompleted && e.Badge_Award_Id__c == null && e.Badge_Id__c != null) {
            System.enqueueJob(new BadgeIssueJob(e.Id));
        }
    }
}

Il controllo wasCompleted è importante. Senza di esso, qualsiasi modifica successiva a un record completato farebbe scattare di nuovo la logica. Insieme alla protezione sull’award id vuoto dentro il job, un record può produrre una sola credenziale.

Un limite da rispettare: Salesforce consente di mettere in coda solo un numero ridotto di job Queueable da una singola transazione (50 in contesto sincrono). Se carichi i completamenti in blocco, ad esempio un import di 2.000 righe, il trigger qui sopra raggiungerà quel tetto. Per i caricamenti massivi, o carichi l’elenco con gli award in blocco via CSV dalla dashboard, oppure passi a una classe Batchable con Database.AllowsCallouts che elabora pochi record per scope.

API keys list showing the masked prefix

Gestione degli errori e nuovi tentativi

L’API restituisce i codici di stato standard, e ciascuno richiede una reazione diversa da parte del tuo codice:

StatoSignificatoCosa fare
400Un campo mancava o non era valido, incluso un nome del destinatario sotto i cinque caratteriNon riprovare. Correggi i dati e scrivi il messaggio in Badge_Error__c.
401Chiave API errata o mancanteControlla la Named Credential. Riprovare non servirà finché non è corretta.
402Credenziali o quota esaurite per il tuo pianoSmetti di emettere e avvisa un amministratore; riprova dopo l’upgrade o dopo il reset della quota.
404Il badgeId non esisteCorreggi Badge_Id__c sul record o sul corso.
5xx o timeoutErrore transitorioPuoi riprovare più tardi in sicurezza.

Poiché il job scrive l’errore sul record e lascia vuoto Badge_Award_Id__c, un nuovo tentativo è semplice come spuntare una checkbox o risalvare il record. Una piccola classe Apex schedulata che cerca le iscrizioni completate senza award id e con un errore transitorio, e le rimette in coda, ti dà una coda di retry automatica senza alcuna infrastruttura aggiuntiva. Limita i tentativi con un campo contatore, in modo che un record irrimediabilmente errato non giri all’infinito.

Controlla il tuo piano prima di collegare tutto alla produzione. Il piano Free copre 1.200 credenziali all’anno; Starter costa $9 al mese e Pro $29 al mese. Un trigger che scatta su ogni Lead consumerebbe in fretta un piano piccolo, quindi limita i criteri ai traguardi reali.

Rendere visibile la credenziale in Salesforce

Una volta compilato Badge_Url__c, puoi usarlo ovunque:

  • Avvisi email e template: inserisci il campo in una email «Congratulazioni, ecco la tua credenziale».
  • Report: un report su Badge_Award_Id__c != null ti dà il numero di emissioni per corso, regione o proprietario dell’account.
  • Experience Cloud: mostra il link nella pagina di certificazione di un partner, così i clienti possono verificarlo da soli.
  • Page layout: aggiungi il campo URL così il supporto può rispondere a «ha ricevuto il suo badge?» con un’occhiata.

La pagina di verifica è pubblica e non richiede registrazione, ed è proprio questo il punto. Un destinatario, un selezionatore o l’ufficio acquisti di un cliente possono aprire il link e confermare chi ha emesso la credenziale, cosa certifica e quando. Per le verifiche automatiche, il JSON-LD Open Badge v2.0 di qualsiasi award è disponibile su https://api.badges.ninja/certify-badge/award/<awardId>.

I destinatari ricevono anche i soliti extra: un URL di verifica univoco, un codice QR, un certificato PDF in formato A4 e un’opzione Add to LinkedIn dal portale destinatari, che consente l’accesso con un magic link anziché con una password.

Perché è meglio di un plugin badge su AppExchange

I plugin per badge su AppExchange tendono ad avere un prezzo per posto Salesforce o per licenza di managed package. Quel costo cresce con le dimensioni del tuo team Salesforce, non con il numero di credenziali che emetti, quindi un team di formazione di dieci persone e un programma da diecimila studenti pagano in modo molto diverso per lo stesso risultato.

Un’integrazione via API ribalta questo modello. Paghi in base al volume di credenziali con un piano fisso, il codice del trigger è tuo da leggere e modificare, e l’output è Open Badge v2.0 standard, quindi i destinatari non restano vincolati a un viewer proprietario. Mantieni anche la libertà di design: il designer visuale offre oltre 80 template, forme, icone e caricamenti personalizzati, invece di un layout fisso dentro un package.

Il compromesso, onestamente: un plugin si installa con un clic e non richiede Apex. Se nessuno nel tuo team può mantenere qualche decina di righe di codice, un plugin o un connettore no-code potrebbe essere più adatto. Se stai confrontando le piattaforme in generale e non le integrazioni, l’analisi sull’alternativa a Credly copre prezzi e funzionalità fianco a fianco.

Testare prima di andare in produzione

Esegui prima l’intero flusso in una sandbox.

  1. Crea un badge di prova e un destinatario di prova con un indirizzo email che controlli.
  2. Nei test Apex, implementa HttpCalloutMock restituendo stato 200 e {"awardId":"test-award-id"}; Salesforce non consente callout reali nel contesto di test, e ti serve quella copertura per il deploy.
  3. Nella sandbox, imposta un’iscrizione su Completed e verifica che Badge_Award_Id__c e Badge_Url__c si compilino entro un minuto.
  4. Apri l’URL in una finestra di navigazione privata per confermare che la pagina pubblica di verifica venga mostrata senza effettuare l’accesso.
  5. Forza un errore (svuota l’id del badge, oppure usa un nome di destinatario sotto i cinque caratteri) e conferma che l’errore finisca in Badge_Error__c.

Ruota la chiave API se mai finisce in un debug log o in uno screenshot. La revoca è immediata dalla pagina API Keys, e dovrai aggiornare solo la External Credential, non il tuo codice.

Per concludere

Lo schema è piccolo: un trigger snello decide quando, un Queueable esegue la callout, una Named Credential custodisce la chiave e tre campi sul record memorizzano l’esito. Funziona per traguardi formativi, livelli dei partner, partecipazione a eventi o qualsiasi cambio di stato tu possa esprimere in Salesforce.

Parti da un solo oggetto e un solo badge, osservalo per una settimana ed estendi quando il comportamento dei retry si adatta ai tuoi dati.

Pronto a emettere la tua prima credenziale verificabile? Inizia gratis su badges.ninja — designer visuale, pagina pubblica di verifica, certificato PDF, output Open Badge v2.0. Nessuna carta di credito richiesta.

Nacho Coll

Informazioni sull'autore

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.

Torna al Blog

Articoli correlati