Open Badges no Salesforce: emitir credenciais verificáveis a partir de gatilhos do CRM

Desencadeia a emissão de Open Badges a partir de qualquer evento de um objeto do Salesforce — Lead ganho, Conta promovida, marco de formação — com um callout Apex para a API do Badges Ninja.

Nacho Coll Por Atualizado 10 min de leitura
Desencadeia a emissão de Open Badges a partir de qualquer evento de um objeto do Salesforce — Lead ganho, Conta promovida, marco de formação — com um callout Apex para a API do Badges Ninja.

A maioria das equipas de formação já regista no Salesforce quem concluiu o quê. Uma inscrição num curso passa a “Completed”, uma conta de parceiro atinge um nível de certificação, um cliente termina o onboarding. O registo muda e, uma semana depois, alguém exporta uma lista e envia os certificados por email, à mão.

Este guia mostra como fechar essa lacuna: quando um registo atinge o estado que te interessa, o Salesforce chama a API de https://badges.ninja, é emitida uma credencial Open Badge v2.0 e a ligação de verificação fica guardada no próprio registo. Depois, podes colocar essa ligação num modelo de email, num relatório ou numa página do Experience Cloud.

Vais precisar de um administrador do Salesforce (ou de uma sandbox de desenvolvimento), de um emblema já desenhado em https://badges.ninja e de uma chave de API. Se nunca chamaste a API, lê primeiro o guia rápido da API; demora dez minutos.

Quando um gatilho do CRM é a ferramenta certa

Antes de escreveres Apex, vê se precisas mesmo dele. Se emites credenciais em lote, uma vez por semestre ou por trimestre, o carregamento de CSV no painel é mais rápido e não exige código. A via Salesforce compensa quando:

  • A emissão deve acontecer no instante em que o registo muda, sem qualquer passo humano.
  • Os dados de “quem conquistou” já vivem no Salesforce e não queres uma segunda fonte de verdade.
  • Queres guardar a ligação da credencial no Contacto, no Lead ou no registo de formação personalizado, para efeitos de relatórios.

Se a tua ferramenta de workflow é o Zapier e não o Apex, a mesma chamada à API funciona lá também; vê automatização de Open Badges com o Zapier.

O que vais construir

O fluxo tem quatro peças:

  1. Um emblema desenhado no designer visual. Copia o respetivo id (o id devolvido por GET /badges).
  2. Uma chave de API criada na página de Chaves de API do teu painel. A chave só é mostrada uma vez, por isso guarda-a logo.
  3. Uma Named Credential no Salesforce que guarda a chave, para que nunca apareça no código.
  4. Um gatilho Apex mais uma classe Queueable que emite a atribuição e escreve a ligação da atribuição de volta no registo.

Newly created API key revealed once

Passo 1: Guardar a chave numa Named Credential

Codificar uma chave de API diretamente no Apex é o erro a evitar. Em Setup, cria uma External Credential com um cabeçalho personalizado e, a seguir, uma Named Credential que aponte para o anfitrião da API:

  • URL: https://api.badges.ninja
  • Nome do cabeçalho personalizado: X-Api-Key
  • Valor do cabeçalho personalizado: a tua chave, no formato bws_YOUR_KEY_HERE
  • Nome da Named Credential: BadgesNinja

Dá ao permission set que executa a tua automatização acesso ao principal da External Credential. Com uma Named Credential configurada, não precisas de uma Remote Site Setting separada; o Salesforce autoriza o callout através da credencial. Se a tua organização ainda usa Named Credentials legadas ou um endpoint simples, adiciona https://api.badges.ninja como Remote Site Setting e envia o cabeçalho a partir do código.

Passo 2: Adicionar campos ao registo

No objeto que representa a conquista (vamos usar um objeto personalizado chamado Training_Enrollment__c), adiciona:

  • Badge_Id__c (Text): o emblema a emitir. Guardá-lo por curso permite que um só gatilho sirva vários emblemas.
  • Badge_Award_Id__c (Text, unique): o id da atribuição devolvido pela API.
  • Badge_Url__c (URL): a página pública de verificação.
  • Badge_Error__c (Long Text): o último erro, para que os administradores vejam as falhas sem abrir os registos de depuração.

O campo Badge_Award_Id__c serve duas funções. É a tua proteção contra duplicados: o gatilho só emite quando este campo está vazio.

Passo 3: O callout Apex

Um gatilho não pode fazer um callout diretamente, porque não são permitidos callouts na mesma transação que um DML ainda não confirmado. Entrega o trabalho a um Queueable que implemente Database.AllowsCallouts.

O contrato da API para POST /awards é curto. O corpo leva badgeId, um objeto recipient aninhado com name e email, e um issuedOn obrigatório. Uma chamada bem-sucedida devolve HTTP 200 com { "awardId": "..." }. O nome do destinatário tem de ter pelo menos cinco caracteres, pelo que um Contacto chamado “Al Li” seria rejeitado; valida antes de chamares.

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;
    }
}

Há dois detalhes a notar. Primeiro, issuedOn está em milissegundos desde a época Unix, que é exatamente o que Datetime.getTime() devolve. Segundo, a resposta não inclui o URL de verificação, por isso o código constrói-o a partir do id da atribuição: https://badges.ninja/awards/<awardId>.

Passo 4: O gatilho

Mantém o gatilho enxuto. Ele só decide que registos qualificam e coloca um job em fila por cada registo.

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));
        }
    }
}

A verificação wasCompleted é importante. Sem ela, qualquer edição posterior a um registo concluído voltaria a disparar a lógica. Combinada com a proteção do id de atribuição vazio dentro do job, um registo só pode gerar uma única credencial.

Um limite a respeitar: o Salesforce permite que um número reduzido de jobs Queueable seja colocado em fila numa única transação (50 em contexto síncrono). Se carregares conclusões em massa, digamos uma importação de 2000 linhas, o gatilho acima vai bater nesse teto. Para cargas em massa, ou carregas a lista através das atribuições em massa por CSV do painel, ou passas a uma classe Batchable com Database.AllowsCallouts que processe alguns registos por âmbito.

API keys list showing the masked prefix

Tratamento de erros e novas tentativas

A API devolve códigos de estado standard, e cada um pede uma resposta diferente do teu código:

StatusSignificadoO que fazer
400Um campo em falta ou inválido, incluindo um nome de destinatário com menos de cinco caracteresNão voltes a tentar. Corrige os dados e escreve a mensagem em Badge_Error__c.
401Chave de API errada ou em faltaVerifica a Named Credential. Tentar de novo não ajuda enquanto não estiver corrigida.
402Sem credenciais ou quota esgotada no teu planoPára de emitir e alerta um administrador; tenta de novo após a atualização do plano ou após a reposição da quota.
404O badgeId não existeCorrige Badge_Id__c no registo ou no curso.
5xx ou timeoutFalha transitóriaPodes tentar de novo mais tarde.

Como o job escreve o erro no registo e deixa Badge_Award_Id__c vazio, repetir é tão simples como alternar uma caixa de seleção ou voltar a guardar o registo. Uma pequena classe Apex agendada que procure inscrições concluídas sem id de atribuição e com um erro transitório, e as volte a colocar em fila, dá-te uma fila de novas tentativas automática sem infraestrutura adicional. Limita as tentativas com um campo contador, para que um registo permanentemente errado não fique em ciclo para sempre.

Verifica o teu plano antes de ligares isto à produção. O plano Free cobre 1200 credenciais por ano; o Starter custa $9 por mês e o Pro $29 por mês. Um gatilho que dispare em cada Lead esgotaria depressa um plano pequeno, por isso restringe os critérios a conquistas reais.

Tornar a credencial visível no Salesforce

Assim que Badge_Url__c estiver preenchido, podes usá-lo em qualquer lado:

  • Alertas e modelos de email: junta o campo a um email do tipo “Parabéns, aqui está a tua credencial”.
  • Relatórios: um relatório sobre Badge_Award_Id__c != null dá-te contagens de emissão por curso, região ou responsável da conta.
  • Experience Cloud: mostra a ligação na página de certificação de um parceiro, para que os compradores a possam verificar por si.
  • Page layouts: adiciona o campo de URL para que o suporte responda a “ela recebeu o emblema?” num relance.

A página de verificação é pública e não exige registo, e é esse o objetivo. Um destinatário, um responsável de contratação ou a equipa de compras de um cliente pode abrir a ligação e confirmar quem emitiu a credencial, o que ela certifica e quando. Para verificações automáticas, o JSON-LD Open Badge v2.0 de qualquer atribuição está disponível em https://api.badges.ninja/certify-badge/award/<awardId>.

Os destinatários recebem ainda os extras habituais: um URL de verificação único, um código QR, um certificado PDF em A4 e uma opção de Adicionar ao LinkedIn a partir do portal do destinatário, que inicia sessão com uma ligação mágica em vez de uma palavra-passe.

Porque é que isto vence um plugin de emblemas da AppExchange

Os plugins de emblemas da AppExchange tendem a ser cobrados por utilizador do Salesforce ou por licença de pacote gerido. Esse custo cresce com o tamanho da tua equipa Salesforce, não com o número de credenciais que emites, pelo que uma equipa de formação de dez pessoas e um programa com dez mil formandos pagam de forma muito diferente pelo mesmo resultado.

Uma integração por API inverte esse modelo. Pagas por volume de credenciais num plano fixo, o código do gatilho é teu para ler e alterar, e o resultado é Open Badge v2.0 standard, pelo que os destinatários não ficam presos a um visualizador proprietário. Manténs também a liberdade de design: o designer visual oferece mais de 80 modelos, formas, ícones e carregamentos personalizados, em vez de um layout fixo dentro de um pacote.

A troca, com honestidade: um plugin instala-se com um clique e não precisa de Apex. Se ninguém na tua equipa conseguir manter algumas dezenas de linhas de código, um plugin ou um conector sem código pode ser a melhor opção. Se estás a comparar plataformas em geral e não integrações, a análise alternativa ao Credly cobre preços e diferenças de funcionalidades lado a lado.

Testar antes de entrar em produção

Executa primeiro todo o fluxo numa sandbox.

  1. Cria um emblema de teste e um destinatário de teste com um endereço de email que controles.
  2. Nos testes Apex, implementa HttpCalloutMock a devolver o estado 200 e {"awardId":"test-award-id"}; o Salesforce não permite callouts reais em contexto de teste, e precisas dessa cobertura para fazer deploy.
  3. Na sandbox, define uma inscrição como Completed e verifica que Badge_Award_Id__c e Badge_Url__c ficam preenchidos num minuto.
  4. Abre o URL numa janela privada do navegador para confirmar que a página pública de verificação é apresentada sem iniciar sessão.
  5. Força uma falha (deixa o id do emblema em branco ou usa um nome de destinatário com menos de cinco caracteres) e confirma que o erro aparece em Badge_Error__c.

Roda a chave de API se alguma vez ela vazar para um registo de depuração ou para uma captura de ecrã. A revogação é imediata a partir da página de Chaves de API, e só precisas de atualizar a External Credential, não o teu código.

Para concluir

O padrão é pequeno: um gatilho enxuto decide quando, um Queueable faz o callout, uma Named Credential guarda a chave e três campos no registo guardam o resultado. Funciona para marcos de formação, níveis de parceiros, presença em eventos ou qualquer mudança de estado que consigas exprimir no Salesforce.

Começa com um objeto e um emblema, observa durante uma semana e expande quando o comportamento das novas tentativas se adequar aos teus dados.

Pronto para emitir a tua primeira credencial verificável? Começa grátis em badges.ninja — designer visual, página pública de verificação, certificado em PDF, saída Open Badge v2.0. Sem cartão de crédito.

Nacho Coll

Sobre o autor

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.

Voltar ao Blog

Artigos Relacionados