# Como integrar validação de CPF em Nuvemshop com Nexo SDK

> Tutorial para integrar validação de CPF em lojas Nuvemshop usando o Nexo SDK para criar aplicativos com a API CPFHub.

**Publicado:** 05/08/2026
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-integrar-validacao-de-cpf-em-nuvemshop-com-nexo-sdk

---


Para integrar validação de CPF em lojas Nuvemshop, use o Nexo SDK para criar um aplicativo que roda como iframe no painel administrativo e injeta um script no checkout. O backend do app recebe o CPF, consulta `GET https://api.cpfhub.io/cpf/{CPF}` com o header `x-api-key` e retorna o resultado ao frontend — tudo em aproximadamente 900ms. A documentação oficial do [Nexo SDK](https://tiendanube.github.io/api-documentation/intro) detalha os escopos e eventos disponíveis para comunicação com a plataforma.

---

## O que é o Nexo SDK

O Nexo SDK é o framework oficial da Nuvemshop para desenvolvimento de aplicativos. Ele fornece:

- **Nimbus Design System**: componentes de UI padronizados.
- **Nexo**: biblioteca para comunicação entre o aplicativo e o admin da Nuvemshop.
- **API REST**: endpoints para acessar dados da loja.
- **Webhooks**: notificações de eventos da loja.

O aplicativo é executado como um iframe dentro do painel administrativo da Nuvemshop e pode interagir com o checkout via scripts externos.

---

## Criando o projeto com Nexo

### Scaffold do projeto

```bash
# Instalar o CLI da Nuvemshop
npm install -g @tiendanube/cli

# Criar o projeto
tiendanube app create cpf-validator
cd cpf-validator

# Instalar dependências
npm install @tiendanube/nexo @nimbus-ds/components axios
```

### Estrutura do projeto

```
cpf-validator/
 src/
 pages/
 index.tsx
 settings.tsx
 components/
 CpfValidator.tsx
 api/
 validate-cpf.ts
 lib/
 nexo.ts
 public/
 checkout-script.js
 package.json
```

---

## Configurando a comunicação com Nexo

```typescript
// src/lib/nexo.ts
import nexo from "@tiendanube/nexo";

const instance = nexo.create({
 clientId: process.env.NEXT_PUBLIC_NUVEMSHOP_CLIENT_ID!,
 log: process.env.NODE_ENV === "development",
});

export default instance;
```

---

## Criando o endpoint de validação

O backend do aplicativo recebe as requisições do frontend e consulta a API da CPFHub:

```typescript
// src/api/validate-cpf.ts
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
 req: NextApiRequest,
 res: NextApiResponse
) {
 if (req.method !== "POST") {
 return res.status(405).json({ message: "Método não permitido" });
 }

 const { cpf } = req.body;
 const cleanCpf = String(cpf).replace(/\D/g, "");

 if (cleanCpf.length !== 11) {
 return res.status(400).json({
 success: false,
 message: "CPF deve conter 11 dígitos.",
 });
 }

 const apiKey = process.env.CPFHUB_API_KEY;

 if (!apiKey) {
 return res.status(500).json({
 success: false,
 message: "Chave de API não configurada.",
 });
 }

 const controller = new AbortController();
 const timeoutId = setTimeout(() => controller.abort(), 10000);

 try {
 const response = await fetch(
 `https://api.cpfhub.io/cpf/${cleanCpf}`,
 {
 method: "GET",
 headers: {
 "x-api-key": apiKey,
 Accept: "application/json",
 },
 signal: controller.signal,
 }
 );

 clearTimeout(timeoutId);
 const data = await response.json();

 return res.status(response.ok ? 200 : response.status).json(data);
 } catch (error: any) {
 clearTimeout(timeoutId);

 if (error.name === "AbortError") {
 return res.status(504).json({
 success: false,
 message: "Tempo de resposta excedido.",
 });
 }

 return res.status(500).json({
 success: false,
 message: "Erro ao consultar CPF.",
 });
 }
}
```

---

## Componente de validação com Nimbus

```tsx
// src/components/CpfValidator.tsx
import React, { useState, useRef, useCallback } from "react";
import {
 Box,
 Input,
 Text,
 Card,
 Spinner,
 Icon,
 Alert,
} from "@nimbus-ds/components";
import { CheckCircleIcon, ExclamationTriangleIcon } from "@nimbus-ds/icons";

interface CpfData {
 cpf: string;
 name: string;
 birthDate: string;
 gender: string;
}

const CpfValidator: React.FC = () => {
 const [cpf, setCpf] = useState("");
 const [loading, setLoading] = useState(false);
 const [result, setResult] = useState<CpfData | null>(null);
 const [error, setError] = useState("");
 const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

 const formatCpf = (value: string): string => {
 const digits = value.replace(/\D/g, "").slice(0, 11);
 if (digits.length > 9)
 return digits.replace(
 /(\d{3})(\d{3})(\d{3})(\d{1,2})/,
 "$1.$2.$3-$4"
 );
 if (digits.length > 6)
 return digits.replace(/(\d{3})(\d{3})(\d{1,3})/, "$1.$2.$3");
 if (digits.length > 3)
 return digits.replace(/(\d{3})(\d{1,3})/, "$1.$2");
 return digits;
 };

 const validate = useCallback(async (cleanCpf: string) => {
 setLoading(true);
 setError("");
 setResult(null);

 try {
 const controller = new AbortController();
 const timeoutId = setTimeout(() => controller.abort(), 12000);

 const response = await fetch("/api/validate-cpf", {
 method: "POST",
 headers: { "Content-Type": "application/json" },
 body: JSON.stringify({ cpf: cleanCpf }),
 signal: controller.signal,
 });

 clearTimeout(timeoutId);
 const data = await response.json();

 if (data.success) {
 setResult(data.data);
 } else {
 setError(data.message || "CPF não encontrado.");
 }
 } catch (err: any) {
 setError(
 err.name === "AbortError"
 ? "Tempo excedido."
 : "Erro ao validar."
 );
 } finally {
 setLoading(false);
 }
 }, []);

 const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
 const formatted = formatCpf(e.target.value);
 setCpf(formatted);

 const digits = formatted.replace(/\D/g, "");
 if (debounceRef.current) clearTimeout(debounceRef.current);

 if (digits.length === 11) {
 debounceRef.current = setTimeout(() => validate(digits), 500);
 } else {
 setResult(null);
 setError("");
 }
 };

 return (
 <Box display="flex" flexDirection="column" gap="4">
 <Input
 label="CPF"
 placeholder="000.000.000-00"
 value={cpf}
 onChange={handleChange}
 append={
 loading ? (
 <Spinner size="small" />
 ) : result ? (
 <Icon source={<CheckCircleIcon />} color="success-interactive" />
 ) : null
 }
 />

 {error && (
 <Alert appearance="danger">
 <Text>{error}</Text>
 </Alert>
 )}

 {result && (
 <Card>
 <Card.Body>
 <Box display="flex" flexDirection="column" gap="2">
 <Text>
 <Text as="span" fontWeight="bold">
 Nome:
 </Text>{" "}
 {result.name}
 </Text>
 <Text>
 <Text as="span" fontWeight="bold">
 Data de Nascimento:
 </Text>{" "}
 {result.birthDate}
 </Text>
 <Text>
 <Text as="span" fontWeight="bold">
 Genero:
 </Text>{" "}
 {result.gender}
 </Text>
 </Box>
 </Card.Body>
 </Card>
 )}
 </Box>
 );
};

export default CpfValidator;
```

---

## Página principal do aplicativo

```tsx
// src/pages/index.tsx
import React, { useEffect } from "react";
import { Page, Layout, Box } from "@nimbus-ds/components";
import nexo from "../lib/nexo";
import CpfValidator from "../components/CpfValidator";

const HomePage: React.FC = () => {
 useEffect(() => {
 nexo.connect().then(() => {
 console.log("Conectado ao Nexo");
 });
 }, []);

 return (
 <Page>
 <Page.Header title="Validador de CPF" />
 <Page.Body>
 <Layout>
 <Layout.Section>
 <Box padding="4">
 <CpfValidator />
 </Box>
 </Layout.Section>
 </Layout>
 </Page.Body>
 </Page>
 );
};

export default HomePage;
```

---

## Script de validação no checkout

Para validar CPF diretamente no checkout da Nuvemshop, crie um script externo que é injetado via configuração do aplicativo:

```javascript
// public/checkout-script.js
(function () {
 "use strict";

 var API_URL = "https://seu-app.vercel.app/api/validate-cpf";
 var debounceTimer = null;

 function init() {
 var observer = new MutationObserver(function () {
 var cpfField = document.querySelector(
 'input[name="cpf"], input[data-checkout-cpf]'
 );

 if (cpfField && !cpfField.dataset.cpfhubBound) {
 cpfField.dataset.cpfhubBound = "true";
 bindValidation(cpfField);
 observer.disconnect();
 }
 });

 observer.observe(document.body, {
 childList: true,
 subtree: true,
 });
 }

 function bindValidation(field) {
 var feedback = document.createElement("div");
 feedback.style.cssText =
 "font-size:12px;margin-top:4px;min-height:16px;";
 field.parentNode.appendChild(feedback);

 field.addEventListener("input", function () {
 if (debounceTimer) clearTimeout(debounceTimer);
 debounceTimer = setTimeout(function () {
 var cpf = field.value.replace(/\D/g, "");
 if (cpf.length === 11) {
 validateCpf(cpf, feedback);
 }
 }, 700);
 });
 }

 function validateCpf(cpf, feedback) {
 feedback.textContent = "Validando...";
 feedback.style.color = "#666";

 var controller = new AbortController();
 var timeoutId = setTimeout(function () {
 controller.abort();
 }, 12000);

 fetch(API_URL, {
 method: "POST",
 headers: { "Content-Type": "application/json" },
 body: JSON.stringify({ cpf: cpf }),
 signal: controller.signal,
 })
 .then(function (r) {
 clearTimeout(timeoutId);
 return r.json();
 })
 .then(function (data) {
 if (data.success) {
 feedback.textContent = "CPF valido";
 feedback.style.color = "#059669";
 } else {
 feedback.textContent = data.message || "CPF invalido";
 feedback.style.color = "#dc2626";
 }
 })
 .catch(function () {
 clearTimeout(timeoutId);
 feedback.textContent = "Erro na validacao";
 feedback.style.color = "#dc2626";
 });
 }

 init();
})();
```

---

## Deploy e publicação

```bash
# Deploy no Vercel
npm run build
vercel --prod

# Registrar o aplicativo na Nuvemshop
# Acesse: https://partners.nuvemshop.com.br
# Configure a URL do app e os escopos necessários
```

---

## Perguntas frequentes

### Como o Nexo SDK comunica o aplicativo com o painel da Nuvemshop?

O Nexo SDK usa `postMessage` para estabelecer um canal seguro entre o iframe do aplicativo e o admin da Nuvemshop. A chamada `nexo.connect()` inicia o handshake; após a conexão, o app pode disparar ações nativas da plataforma, como navegar para seções do painel ou exibir notificações. Consulte a [documentação da API Nuvemshop](https://tiendanube.github.io/api-documentation/intro) para ver todos os eventos suportados.

### Qual é a latência esperada ao validar CPF via Nexo SDK?

A API CPFHub.io responde em aproximadamente 900ms. Como o frontend faz um POST para o endpoint do próprio app (Next.js), que por sua vez consulta a CPFHub, o ciclo total fica em torno de 1-1,5 segundo em condições normais. O debounce de 500ms no componente evita disparos desnecessários enquanto o usuário ainda está digitando.

### O que acontece se o limite de consultas do plano gratuito for atingido?

O plano gratuito cobre 50 consultas por mês. Ao atingir esse limite, a API não bloqueia as requisições: cada consulta adicional é cobrada a R$0,15. Para aplicativos com volume maior, o plano Pro inclui 1.000 consultas mensais por R$149, com o mesmo custo por excedente.

### Como garantir conformidade com a LGPD ao usar uma API de CPF em um app Nuvemshop?

Use o CPF apenas para a finalidade declarada ao titular, armazene apenas o necessário (não guarde o CPF cru se um token bastar), implemente controle de acesso aos logs de consulta e documente a base legal para o tratamento. A [ANPD](https://www.gov.br/anpd) orienta que dados de identificação devem ser tratados com o princípio da necessidade.

### 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)
- [Como consumir API de CPF em Tray Commerce via API de integração](https://cpfhub.io/blog/como-consumir-api-de-cpf-em-tray-commerce-via-api-de-integracao)
- [Como integrar validação de CPF em VTEX IO com service worker](https://cpfhub.io/blog/como-integrar-validacao-de-cpf-em-vtex-io-com-service-worker)
- [Boas práticas para consumir APIs de CPF de forma segura](https://cpfhub.io/blog/boas-praticas-consumir-apis-cpf-segura)

---

## Conclusão

O Nexo SDK da Nuvemshop oferece uma plataforma completa para criar aplicativos que estendem as funcionalidades da loja. Ao combinar o Nexo com a API do [**CPFHub.io**](https://www.cpfhub.io/)

A API da CPFHub entrega respostas em aproximadamente 900ms, com uptime de 99,9% e conformidade total com a LGPD -- requisitos essenciais para aplicativos no ecossistema Nuvemshop.

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

