Como integrar validação de CPF em Salesforce Apex para CRM corporativo

Aprenda a integrar a API de consulta de CPF em Salesforce usando Apex com callouts HTTP, triggers e Lightning Web Components para CRM corporativo.

Lucas Vieira
Lucas Vieira
··7 min de leitura
Como integrar validação de CPF em Salesforce Apex para CRM corporativo

Para validar CPF em Salesforce com Apex, registre https://api.cpfhub.io como Remote Site Setting e use HttpRequest com o header x-api-key para chamar GET https://api.cpfhub.io/cpf/{CPF}. A CPFHub.io retorna nome, gênero e data de nascimento com latência de ~900ms — compatível com o timeout padrão de callouts no Apex. Triggers que disparam a validação devem usar métodos @future(callout=true) para não bloquear a transação síncrona, e a chave de API deve ficar em Custom Metadata Types com campo criptografado ou em Named Credentials.


Configuração de Remote Site Setting

Antes de fazer callouts para a API da CPFHub.io, registre o endpoint como Remote Site:

  1. Acesse Setup > Security > Remote Site Settings.
  2. Crie um novo registro:

Sem essa configuração, o Salesforce bloqueia todas as chamadas HTTP externas.


Armazenamento seguro da chave de API

Use Named Credentials ou Custom Metadata Types para armazenar a chave. Consulte a documentação oficial do Apex sobre callouts para boas práticas de segurança em integrações HTTP.

Named Credential (recomendado)

  1. Acesse Setup > Security > Named Credentials.
  2. Crie uma Named Credential:
  • Label: CPFHub API
  • URL: https://api.cpfhub.io
  • Identity Type: Named Principal
  • Authentication Protocol: No Authentication (usaremos header customizado)

Custom Metadata Type (alternativa)

Crie um Custom Metadata Type chamado API_Config__mdt com campos:

  • API_Key__c (Text, Encrypted)
  • Base_URL__c (URL)
  • Timeout_ms__c (Number)

Classe Apex de serviço

A classe que encapsula a comunicação com a API:

// CPFHubService.cls
public with sharing class CPFHubService {

    private static final String API_BASE_URL = 'https://api.cpfhub.io/cpf';
    private static final Integer TIMEOUT_MS = 10000;

    public class CPFResult {
    @AuraEnabled public Boolean valido;
    @AuraEnabled public String nome;
    @AuraEnabled public String cpf;
    @AuraEnabled public String genero;
    @AuraEnabled public String dataNascimento;
    @AuraEnabled public String erro;
    }

    private static String getApiKey() {
    List<API_Config__mdt> configs = [
    SELECT API_Key__c
    FROM API_Config__mdt
    WHERE DeveloperName = 'CPFHub'
    LIMIT 1
    ];

    if (configs.isEmpty()) {
    throw new CalloutException(
    'Configuracao da API CPFHub nao encontrada.'
    );
    }

    return configs[0].API_Key__c;
    }

    @AuraEnabled(cacheable=false)
    public static CPFResult consultarCPF(String cpfNumero) {
    CPFResult resultado = new CPFResult();

    String cpfLimpo = cpfNumero.replaceAll('[^0-9]', '');

    if (cpfLimpo.length() != 11) {
    resultado.valido = false;
    resultado.erro = 'CPF deve conter 11 digitos';
    return resultado;
    }

    String apiKey = getApiKey();

    HttpRequest req = new HttpRequest();
    req.setEndpoint(API_BASE_URL + '/' + cpfLimpo);
    req.setMethod('GET');
    req.setHeader('x-api-key', apiKey);
    req.setHeader('Accept', 'application/json');
    req.setTimeout(TIMEOUT_MS);

    try {
    Http http = new Http();
    HttpResponse res = http.send(req);

    if (res.getStatusCode() != 200) {
    resultado.valido = false;
    resultado.erro = 'Erro HTTP: ' + res.getStatusCode();
    return resultado;
    }

    Map<String, Object> responseBody =
    (Map<String, Object>) JSON.deserializeUntyped(res.getBody());

    Boolean success = (Boolean) responseBody.get('success');

    if (!success) {
    resultado.valido = false;
    resultado.erro = 'CPF nao encontrado';
    return resultado;
    }

    Map<String, Object> data =
    (Map<String, Object>) responseBody.get('data');

    resultado.valido = true;
    resultado.cpf = (String) data.get('cpf');
    resultado.nome = (String) data.get('name');
    resultado.genero = (String) data.get('gender');
    resultado.dataNascimento = (String) data.get('birthDate');

    return resultado;

    } catch (CalloutException e) {
    resultado.valido = false;
    resultado.erro = 'Erro na chamada: ' + e.getMessage();
    return resultado;
    }
    }
}

Trigger para validação automática

Crie um trigger que válida o CPF quando um Contact e criado ou atualizado. Como callouts não podem ser feitos em triggers sincronos, use um método @future:

// CPFValidationTrigger.trigger
trigger CPFValidationTrigger on Contact (after insert, after update) {
    Set<Id> contactIds = new Set<Id>();

    for (Contact c : Trigger.new) {
    if (Trigger.isInsert ||
    (Trigger.isUpdate &&
    c.CPF__c != Trigger.oldMap.get(c.Id).CPF__c)) {
    if (String.isNotBlank(c.CPF__c)) {
    contactIds.add(c.Id);
    }
    }
    }

    if (!contactIds.isEmpty()) {
    CPFValidationHandler.validarCPFsAsync(contactIds);
    }
}
// CPFValidationHandler.cls
public with sharing class CPFValidationHandler {

    @future(callout=true)
    public static void validarCPFsAsync(Set<Id> contactIds) {
    List<Contact> contacts = [
    SELECT Id, CPF__c, FirstName, LastName
    FROM Contact
    WHERE Id IN :contactIds
    ];

    List<Contact> aAtualizar = new List<Contact>();

    for (Contact c : contacts) {
    CPFHubService.CPFResult resultado =
    CPFHubService.consultarCPF(c.CPF__c);

    Contact atualizado = new Contact(Id = c.Id);

    if (resultado.valido) {
    atualizado.CPF_Status__c = 'Validado';
    atualizado.CPF_Nome_Validado__c = resultado.nome;
    atualizado.CPF_Genero__c = resultado.genero;
    atualizado.CPF_Data_Nascimento__c = resultado.dataNascimento;
    atualizado.CPF_Validado_Em__c = DateTime.now();
    } else {
    atualizado.CPF_Status__c = 'Invalido';
    atualizado.CPF_Erro_Validacao__c = resultado.erro;
    atualizado.CPF_Validado_Em__c = DateTime.now();
    }

    aAtualizar.add(atualizado);
    }

    if (!aAtualizar.isEmpty()) {
    update aAtualizar;
    }
    }
}

Lightning Web Component para consulta manual

Crie um LWC para que usuários consultem CPF diretamente na interface do Salesforce:

// cpfValidator.js
import { LightningElement, track } from 'lwc';
import consultarCPF from '@salesforce/apex/CPFHubService.consultarCPF';

export default class CpfValidator extends LightningElement {
    @track cpf = '';
    @track resultado = null;
    @track erro = null;
    @track carregando = false;

    handleInputChange(event) {
    this.cpf = event.target.value;
    this.resultado = null;
    this.erro = null;
    }

    async handleConsultar() {
    if (!this.cpf) return;

    this.carregando = true;
    this.resultado = null;
    this.erro = null;

    try {
    const res = await consultarCPF({ cpfNumero: this.cpf });

    if (res.valido) {
    this.resultado = res;
    } else {
    this.erro = res.erro;
    }
    } catch (error) {
    this.erro = 'Erro inesperado: ' + error.body.message;
    } finally {
    this.carregando = false;
    }
    }
}
<!-- cpfValidator.html -->
<template>
    <lightning-card title="Validar CPF" icon-name="standard:contact">
    <div class="slds-p-horizontal_medium">
    <lightning-input
    label="CPF"
    value={cpf}
    onchange={handleInputChange}
    placeholder="000.000.000-00"
    maxlength="14">
    </lightning-input>

    <lightning-button
    label="Consultar"
    variant="brand"
    onclick={handleConsultar}
    disabled={carregando}
    class="slds-m-top_medium">
    </lightning-button>

    <template if:true={carregando}>
    <lightning-spinner size="small" class="slds-m-top_medium">
    </lightning-spinner>
    </template>

    <template if:true={erro}>
    <div class="slds-notify slds-notify_alert slds-theme_error slds-m-top_medium">
    {erro}
    </div>
    </template>

    <template if:true={resultado}>
    <div class="slds-box slds-m-top_medium">
    <p><strong>Nome:</strong> {resultado.nome}</p>
    <p><strong>Genero:</strong> {resultado.genero}</p>
    <p><strong>Nascimento:</strong> {resultado.dataNascimento}</p>
    </div>
    </template>
    </div>
    </lightning-card>
</template>

Testes Apex

Testes unitarios são obrigatórios no Salesforce para deploy em produção:

// CPFHubServiceTest.cls
@isTest
public class CPFHubServiceTest {

    @isTest
    static void testCPFInvalido() {
    CPFHubService.CPFResult resultado =
    CPFHubService.consultarCPF('12345');

    System.assertEquals(false, resultado.valido);
    System.assertEquals('CPF deve conter 11 digitos', resultado.erro);
    }

    @isTest
    static void testCPFConsultaComMock() {
    Test.setMock(HttpCalloutMock.class, new CPFHubMock());

    Test.startTest();
    CPFHubService.CPFResult resultado =
    CPFHubService.consultarCPF('12345678901');
    Test.stopTest();

    System.assertEquals(true, resultado.valido);
    System.assertEquals('Joao da Silva', resultado.nome);
    }

    private class CPFHubMock implements HttpCalloutMock {
    public HTTPResponse respond(HTTPRequest req) {
    HttpResponse res = new HttpResponse();
    res.setHeader('Content-Type', 'application/json');
    res.setStatusCode(200);
    res.setBody('{"success":true,"data":{"cpf":"12345678901",' +
    '"name":"Joao da Silva","nameUpper":"JOAO DA SILVA",' +
    '"gender":"M","birthDate":"15/06/1990",' +
    '"day":15,"month":6,"year":1990}}');
    return res;
    }
    }
}

Perguntas frequentes

Por que callouts Apex não podem ser feitos dentro de triggers síncronos?

O Salesforce não permite chamadas HTTP em triggers síncronos porque eles rodam dentro de uma transação de banco de dados — um callout bloquearia a transação inteira até a resposta chegar. A solução é usar um método @future(callout=true) que executa de forma assíncrona após a transação principal ser confirmada, como mostrado no CPFValidationHandler acima.

A API CPFHub.io retorna erro 429 ao atingir o limite de consultas no Salesforce?

Não. Quando o limite mensal é ultrapassado, a API não bloqueia nem retorna 429 — ela continua respondendo normalmente e cobra R$0,15 por consulta adicional. O plano gratuito inclui 50 consultas/mês e o Pro 1.000 consultas por R$149. Para orgs com alto volume de Contacts sendo criados, monitore o consumo no painel da CPFHub.io e ajuste o plano conforme o crescimento da base.

Como testar callouts Apex sem fazer chamadas reais à API?

Use Test.setMock(HttpCalloutMock.class, new CPFHubMock()) para substituir a chamada HTTP por uma resposta simulada durante os testes. Isso permite cobrir os cenários de sucesso e falha sem consumir cota da API e sem depender de conectividade externa — requisito para aprovação na Salesforce AppExchange e para deploy em produção com cobertura mínima de 75%.

Como garantir conformidade com a LGPD ao armazenar dados de CPF no Salesforce?

Armazene apenas os campos necessários para a operação — nome validado, gênero e data de nascimento — e use Field-Level Security para restringir o acesso ao campo CPF__c a perfis com necessidade de negócio. Documente o tratamento no Registro de Operações de Tratamento conforme exige a ANPD e configure mascaramento de dados para ambientes de sandbox que copiam dados de produção.


Conclusão

A integração da API de CPF da CPFHub.io

Cadastre-se em cpfhub.io

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

WhatsAppFale conosco via WhatsApp