# 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.

**Publicado:** 02/10/2026
**Autor:** Lucas Vieira
**URL:** https://www.cpfhub.io/blog/como-integrar-validacao-cpf-salesforce-apex-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](https://www.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:
 - **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](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts.htm) 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:

```java
// 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`:

```java
// 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);
 }
}
```

```java
// 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:

```javascript
// 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;
 }
 }
}
```

```html
<!-- 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:

```java
// 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](https://www.gov.br/anpd) e configure mascaramento de dados para ambientes de sandbox que copiam dados de produção.

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [Boas práticas para consumir APIs de CPF de forma segura](https://cpfhub.io/blog/boas-praticas-consumir-apis-cpf-segura)
- [Autenticação em APIs REST: como garantir segurança na consulta de CPF](https://cpfhub.io/blog/autenticacao-apis-rest-seguranca-consulta-cpf)
- [Como integrar validação de CPF em Power Automate para workflows corporativos](https://cpfhub.io/blog/como-integrar-validacao-cpf-power-automate-workflows-corporativos)

---

## Conclusão

A integração da API de CPF da [**CPFHub.io**](https://www.cpfhub.io/)

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/)

