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:
- Acesse Setup > Security > Remote Site Settings.
- Crie um novo registro:
- Remote Site Name: CPFHub_API
- Remote Site URL: https://api.cpfhub.io
- Active: marcado
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)
- Acesse Setup > Security > Named Credentials.
- 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.




