curl -X POST https://admin.kycert.com.br/api/v1/customers \
-H "x-api-key: $KYCERT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "pf",
"doc": "12345678901",
"name": "João Silva",
"email": "joao@example.com",
"run_bureau": true,
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"webhook_url": "https://broker.com/webhooks/kycert"
}'const res = await fetch('https://admin.kycert.com.br/api/v1/customers', {
method: 'POST',
headers: {
'x-api-key': process.env.KYCERT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'pf',
doc: '12345678901',
name: 'João Silva',
email: 'joao@example.com',
run_bureau: true,
template_id: '550e8400-e29b-41d4-a716-446655440000',
webhook_url: 'https://broker.com/webhooks/kycert',
}),
})
const { customer_id, run_id } = await res.json()
console.log(customer_id, run_id)
import requests
url = "https://admin.kycert.com.br/api/v1/customers"
payload = {
"type": "pf",
"doc": "12345678901",
"name": "João Silva",
"email": "joao@example.com",
"run_bureau": True,
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"webhook_url": "https://broker.com/webhooks/kycert",
"external_id": "cust_abc123",
"metadata": { "channel": "app_mobile" }
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'pf',
doc: '12345678901',
name: 'João Silva',
email: 'joao@example.com',
run_bureau: true,
template_id: '550e8400-e29b-41d4-a716-446655440000',
webhook_url: 'https://broker.com/webhooks/kycert',
external_id: 'cust_abc123',
metadata: {channel: 'app_mobile'}
})
};
fetch('https://admin.kycert.com.br/api/v1/customers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://admin.kycert.com.br/api/v1/customers",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'pf',
'doc' => '12345678901',
'name' => 'João Silva',
'email' => 'joao@example.com',
'run_bureau' => true,
'template_id' => '550e8400-e29b-41d4-a716-446655440000',
'webhook_url' => 'https://broker.com/webhooks/kycert',
'external_id' => 'cust_abc123',
'metadata' => [
'channel' => 'app_mobile'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://admin.kycert.com.br/api/v1/customers"
payload := strings.NewReader("{\n \"type\": \"pf\",\n \"doc\": \"12345678901\",\n \"name\": \"João Silva\",\n \"email\": \"joao@example.com\",\n \"run_bureau\": true,\n \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"webhook_url\": \"https://broker.com/webhooks/kycert\",\n \"external_id\": \"cust_abc123\",\n \"metadata\": {\n \"channel\": \"app_mobile\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://admin.kycert.com.br/api/v1/customers")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"pf\",\n \"doc\": \"12345678901\",\n \"name\": \"João Silva\",\n \"email\": \"joao@example.com\",\n \"run_bureau\": true,\n \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"webhook_url\": \"https://broker.com/webhooks/kycert\",\n \"external_id\": \"cust_abc123\",\n \"metadata\": {\n \"channel\": \"app_mobile\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://admin.kycert.com.br/api/v1/customers")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"pf\",\n \"doc\": \"12345678901\",\n \"name\": \"João Silva\",\n \"email\": \"joao@example.com\",\n \"run_bureau\": true,\n \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"webhook_url\": \"https://broker.com/webhooks/kycert\",\n \"external_id\": \"cust_abc123\",\n \"metadata\": {\n \"channel\": \"app_mobile\"\n }\n}"
response = http.request(request)
puts response.read_body{
"customer_id": "661e9511-f3ac-52e5-b827-557766551111",
"object": "customer",
"status": "em_analise",
"type": "pf",
"name": "João Silva",
"email": "joao@example.com",
"doc": "***456789**",
"external_id": "cust_abc123",
"metadata": {
"channel": "app_mobile"
},
"created_at": "2026-06-12T14:00:00Z",
"run_id": "550e8400-e29b-41d4-a716-446655440000",
"bureau_status": "queued",
"livemode": true
}Criar cliente
Cria um cliente (PF ou PJ) no tenant. Opcionalmente dispara um bureau run.
Se run_bureau: true, o comportamento é idêntico ao POST /bureau/runs:
o bureau é executado imediatamente e o resultado é entregue via webhook.
Requer escopo customers:write.
curl -X POST https://admin.kycert.com.br/api/v1/customers \
-H "x-api-key: $KYCERT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "pf",
"doc": "12345678901",
"name": "João Silva",
"email": "joao@example.com",
"run_bureau": true,
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"webhook_url": "https://broker.com/webhooks/kycert"
}'const res = await fetch('https://admin.kycert.com.br/api/v1/customers', {
method: 'POST',
headers: {
'x-api-key': process.env.KYCERT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'pf',
doc: '12345678901',
name: 'João Silva',
email: 'joao@example.com',
run_bureau: true,
template_id: '550e8400-e29b-41d4-a716-446655440000',
webhook_url: 'https://broker.com/webhooks/kycert',
}),
})
const { customer_id, run_id } = await res.json()
console.log(customer_id, run_id)
import requests
url = "https://admin.kycert.com.br/api/v1/customers"
payload = {
"type": "pf",
"doc": "12345678901",
"name": "João Silva",
"email": "joao@example.com",
"run_bureau": True,
"template_id": "550e8400-e29b-41d4-a716-446655440000",
"webhook_url": "https://broker.com/webhooks/kycert",
"external_id": "cust_abc123",
"metadata": { "channel": "app_mobile" }
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'pf',
doc: '12345678901',
name: 'João Silva',
email: 'joao@example.com',
run_bureau: true,
template_id: '550e8400-e29b-41d4-a716-446655440000',
webhook_url: 'https://broker.com/webhooks/kycert',
external_id: 'cust_abc123',
metadata: {channel: 'app_mobile'}
})
};
fetch('https://admin.kycert.com.br/api/v1/customers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://admin.kycert.com.br/api/v1/customers",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'pf',
'doc' => '12345678901',
'name' => 'João Silva',
'email' => 'joao@example.com',
'run_bureau' => true,
'template_id' => '550e8400-e29b-41d4-a716-446655440000',
'webhook_url' => 'https://broker.com/webhooks/kycert',
'external_id' => 'cust_abc123',
'metadata' => [
'channel' => 'app_mobile'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://admin.kycert.com.br/api/v1/customers"
payload := strings.NewReader("{\n \"type\": \"pf\",\n \"doc\": \"12345678901\",\n \"name\": \"João Silva\",\n \"email\": \"joao@example.com\",\n \"run_bureau\": true,\n \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"webhook_url\": \"https://broker.com/webhooks/kycert\",\n \"external_id\": \"cust_abc123\",\n \"metadata\": {\n \"channel\": \"app_mobile\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://admin.kycert.com.br/api/v1/customers")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"pf\",\n \"doc\": \"12345678901\",\n \"name\": \"João Silva\",\n \"email\": \"joao@example.com\",\n \"run_bureau\": true,\n \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"webhook_url\": \"https://broker.com/webhooks/kycert\",\n \"external_id\": \"cust_abc123\",\n \"metadata\": {\n \"channel\": \"app_mobile\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://admin.kycert.com.br/api/v1/customers")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"pf\",\n \"doc\": \"12345678901\",\n \"name\": \"João Silva\",\n \"email\": \"joao@example.com\",\n \"run_bureau\": true,\n \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"webhook_url\": \"https://broker.com/webhooks/kycert\",\n \"external_id\": \"cust_abc123\",\n \"metadata\": {\n \"channel\": \"app_mobile\"\n }\n}"
response = http.request(request)
puts response.read_body{
"customer_id": "661e9511-f3ac-52e5-b827-557766551111",
"object": "customer",
"status": "em_analise",
"type": "pf",
"name": "João Silva",
"email": "joao@example.com",
"doc": "***456789**",
"external_id": "cust_abc123",
"metadata": {
"channel": "app_mobile"
},
"created_at": "2026-06-12T14:00:00Z",
"run_id": "550e8400-e29b-41d4-a716-446655440000",
"bureau_status": "queued",
"livemode": true
}Quando usar
Use este endpoint para registrar um cliente no kycert antes ou independentemente de rodar um bureau. Ideal para fluxos em que o cadastro acontece em etapas separadas da verificação KYC.| Cenário | Endpoint recomendado |
|---|---|
| Cadastrar + verificar em uma chamada | POST /api/v1/customers com run_bureau: true |
| Verificar sem criar cadastro permanente | POST /api/v1/bureau/runs |
| Cadastrar agora, verificar depois | POST /api/v1/customers (sem run_bureau) |
Campos doc, email e external_id
doc
Aceita CPF/CNPJ formatado (123.456.789-01, 12.345.678/0001-99) ou só dígitos — a API normaliza internamente removendo tudo que não for número. Além da quantidade de dígitos (11 para CPF, 14 para CNPJ) e da consistência com o type informado, o dígito verificador é validado. Qualquer uma dessas falhas retorna 400 invalid_document.
email
Obrigatório e validado para type: "pf" — ausente ou em formato inválido retorna 400 invalid_email. Para type: "pj" é opcional; se omitido, nenhuma validação de formato é aplicada.
external_id
Identificador do seu próprio sistema, opcional, até 255 caracteres. Único por tenant — reenviar um external_id já usado por outro cliente retorna 409 external_id_conflict (ver Cliente duplicado abaixo).
Header Idempotency-Key
Envie um UUID único por tentativa de chamada no header Idempotency-Key. Se a mesma chave for reenviada nas próximas 24h, a API retorna a resposta original em cache — sem criar um segundo cliente nem disparar o bureau de novo.
Use sempre que seu código faz retry automático (timeout de rede, erro 5xx, etc.), principalmente com run_bureau: true: sem a chave, um retry num cliente que já existe em rascunho pode disparar um segundo bureau run para o mesmo cliente, cobrado em dobro. Veja Idempotência para o comportamento completo de retry com a mesma chave.
curl -X POST https://admin.kycert.com.br/api/v1/customers \
-H "x-api-key: $KYCERT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f6c5b6a-6f6c-4b6a-9f6c-5b6a6f6c5b6a" \
-d '{ "type": "pf", "doc": "12345678901", "name": "João Silva", "email": "joao@example.com", "run_bureau": true, "template_id": "550e8400-e29b-41d4-a716-446655440000" }'
201) são cacheadas; erros de validação (400) e conflitos (409) não são — uma nova tentativa com a mesma chave após um erro consulta o estado atual normalmente.Idempotency-Key chegarem simultaneamente (em voo ao mesmo tempo, não uma depois da outra terminar), a segunda recebe 409 idempotency_key_in_progress (com header Retry-After em segundos) em vez de processar de novo — evita duplicar a criação do cliente/bureau run quando o cliente HTTP dispara um retry antes de a primeira chamada terminar. Aguarde o Retry-After e tente de novo com a mesma chave: a chamada original, quando concluir, já deixa a resposta em cache para o replay normal.
Cliente duplicado (409)
ChamarPOST /customers com um CPF/CNPJ ou external_id já cadastrado no tenant retorna 409, não 400 — é um conflito de estado, não um erro de validação do request.
{
"error": {
"type": "invalid_request_error",
"code": "customer_already_exists",
"message": "Cliente com este CPF/CNPJ já existe.",
"param": "doc",
"existing_customer_id": "661e9511-f3ac-52e5-b827-557766551111"
}
}
{
"error": {
"type": "invalid_request_error",
"code": "external_id_conflict",
"message": "external_id já existe para este tenant.",
"param": "external_id",
"existing_customer_id": "661e9511-f3ac-52e5-b827-557766551111"
}
}
existing_customer_id — antes de tratar como falha, consulte GET /customers com ?doc= ou ?external_id= (ou GET /customers/ direto com o existing_customer_id) para decidir se o fluxo deve seguir com o cliente já existente em vez de reportar erro ao usuário final.
Campo doc na resposta
O CPF ou CNPJ nunca é retornado em claro. A resposta sempre retorna uma versão mascarada:
- CPF:
***456789** - CNPJ:
**34567890****
Campo run_bureau
Quando run_bureau: true, o bureau é disparado imediatamente após criar o cliente. O comportamento é idêntico ao POST /api/v1/bureau/runs e o resultado chega via webhook — payload completo, eventos disponíveis e política de retry em Visão geral de webhooks. Para sobrescrever a URL de webhook configurada no dashboard só nesta chamada, envie webhook_url.
Veja Conceitos — Run para entender o ciclo de vida de um run, e o guia de onboarding completo para o fluxo fim-a-fim (cadastro → documento → bureau → webhook → consulta).
Template padrão — run_bureau sem template_id
Se o tenant tem um template padrão configurado em Configurações → Bureau no dashboard (um para PF, outro para PJ), template_id é opcional: a API resolve automaticamente o template do type do cliente.
Prioridade de resolução (a primeira que existir vence):
template_idenviado explicitamente- Template do perfil indicado em
onboarding_profile(se o slug for válido) - Template padrão do tenant para o
typedo cliente
400 missing_default_template — o cliente não é criado.
{
"type": "pf",
"doc": "12345678901",
"name": "João Silva",
"email": "joao@example.com",
"run_bureau": true
}
{
"customer_id": "661e9511-f3ac-52e5-b827-557766551111",
"object": "customer",
"status": "em_analise",
"type": "pf",
"name": "João Silva",
"run_id": "550e8400-e29b-41d4-a716-446655440000",
"bureau_status": "queued",
"livemode": true
}
status do cliente usa o vocabulário de decisão de onboarding (em_analise, aprovado, pendencia, recusado) — distinto do status de bureau run, que usa inglês. Veja Status de bureau run vs status de cliente.
livemode reflete se a chamada usou uma chave sk_live_... (produção) ou sk_test_... (sandbox) — veja Sandbox.
Sem template padrão configurado e sem template_id, a mesma chamada retornaria:
{
"error": {
"type": "invalid_request_error",
"code": "missing_default_template",
"message": "Nenhum template padrão configurado para este tipo de cliente. Configure um template padrão em Configurações → Bureau no dashboard, ou envie template_id explicitamente.",
"param": "template_id"
}
}
Campo onboarding_profile
Slug de um perfil de cadastro (o mesmo ?perfil=slug do portal público de onboarding). Quando enviado e válido, o template configurado nesse perfil tem prioridade sobre o default do tenant. Slug inexistente, inativo, ou de outro tenant é ignorado silenciosamente — cai no default do tenant, nunca gera erro.
{
"type": "pf",
"doc": "12345678901",
"name": "João Silva",
"email": "joao@example.com",
"run_bureau": true,
"onboarding_profile": "cadastro-pf-completo"
}
bureau_enabled) do dashboard controla apenas se o portal público dispara bureau sozinho ao final do cadastro — não afeta esta API. run_bureau: true é um pedido explícito do integrador e sempre é respeitado, independente desse toggle.Campo address
Todos os subcampos são opcionais entre si — envie apenas os que tiver. address é aceito e retornado tanto para clientes PF quanto PJ.
| Campo | Regra |
|---|---|
street | String não vazia, até 200 caracteres |
number | String, até 20 caracteres (aceita "S/N", "123", "123A") |
neighborhood | String, até 120 caracteres |
city | String não vazia, até 120 caracteres |
state | Sigla de UF válida, maiúscula (ex: SP, RJ) |
zip | Deve resultar em 8 dígitos após remover caracteres não numéricos (aceita "01310-100" ou "01310100"); é persistido exatamente como enviado |
complement | String, até 200 caracteres |
400 invalid_address com param indicando o subcampo (ex: address.zip).
{
"type": "pf",
"doc": "12345678901",
"name": "Maria Silva",
"email": "maria@example.com",
"address": {
"street": "Av. Paulista",
"number": "1000",
"complement": "Sala 4",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"zip": "01310-100"
}
}
Campos complementares (PF e PJ)
Os mesmos campos profissionais, financeiros e de nacionalidade que o portal público de cadastro coleta — para fechar um cadastro completo via API sem precisar voltar ao dashboard depois. Cada campo é exclusivo dotype indicado; enviá-lo com o type errado retorna 400 (ver tabela).
| Campo | Tipo | Regra |
|---|---|---|
nationality | PF | Deve ser um valor exato da lista canônica de nacionalidades do portal (ex: "Brasileira", "Argentina") |
occupation | PF | String não vazia, até 120 caracteres |
declared_income | PF | String não vazia, até 20 caracteres — aceita valor decimal ("12000.00") ou código de faixa ("10001_20000"), sem normalizar o formato |
trade_name | PJ | String não vazia, até 300 caracteres (nome fantasia) |
annual_revenue | PJ | String não vazia, até 20 caracteres — mesma regra de passthrough de declared_income |
business_activity | PJ | String não vazia, até 120 caracteres — texto livre, não validado contra lista curada |
employee_count | PJ | String não vazia, até 10 caracteres |
declared_income e annual_revenue são apenas armazenados — não acionam recálculo automático de capacidade financeira (motor de renda). Se a empresa precisa que esses valores alimentem risk scoring, isso continua sendo feito manualmente hoje.occupation com type: "pj") retorna 400 invalid_request_error com param indicando o campo:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_occupation",
"message": "occupation é exclusivo para PF.",
"param": "occupation"
}
}
{
"type": "pf",
"doc": "12345678901",
"name": "Maria Silva",
"email": "maria@example.com",
"nationality": "Argentina",
"occupation": "Engenheiro(a) de Software",
"declared_income": "12000.00"
}
{
"type": "pj",
"doc": "12345678000199",
"name": "Kycert Tecnologia Ltda",
"trade_name": "Kycert Tech",
"annual_revenue": "5000000.00",
"business_activity": "Desenvolvimento de software / TI",
"employee_count": "25"
}
Lista de nacionalidades
Mesma lista usada pelo seletor do portal público — envie o valor exatamente como aparece abaixo (Brasileira, Alemã, Angolana, Argentina, Australiana, Austríaca, Belga, Boliviana, Canadense, Chilena, Chinesa, Colombiana, Coreana, Cubana, Dinamarquesa, Egípcia, Equatoriana, Espanhola, Estadunidense, Finlandesa, Francesa, Grega, Guatemalteca, Holandesa, Hondurenha, Húngara, Indiana, Indonésia, Inglesa, Iraniana, Irlandesa, Israelense, Italiana, Jamaicana, Japonesa, Libanesa, Marroquina, Mexicana, Moçambicana, Nigeriana, Norueguesa, Panamenha, Paraguaia, Peruana, Polonesa, Portuguesa, Russa, Salvadorenha, Sul-africana, Sueca, Suíça, Turca, Ucraniana, Uruguaia, Venezuelana, Vietnamita, Outra). Valor fora dessa lista retorna 400 invalid_nationality.
Verificação facial automática
Se o cliente é criado com uma selfie e um documento de identidade suportado anexados (via upload simultâneo — hoje só possível anexando os dois em POST /customers//documents logo em seguida, já quePOST /customers não aceita arquivo), e o tenant tem o módulo “Verificação facial” ativo (master) e a configuração habilitada (tenant), o kycert dispara Face Match e Passive Liveness automaticamente, em background — nunca soma latência à resposta deste endpoint. Não existe parâmetro no payload para ativar ou pular esse disparo por chamada; a decisão é sempre da configuração do tenant.
GET /customers/{id}retorna o campofacial_verificationcom o resultado da rodada mais recente — ver Buscar cliente. Melhor para quem só confere ocasionalmente (polling).- Webhook
biometric_check.completed— notifica assim que a rodada termina. Ver Webhooks. Melhor para quem já automatiza reação a eventos.
Erros
Formato completo do envelope e estratégia de retry em Tratamento de erros. Os códigos deste endpoint:- Autenticação/autorização:
missing_api_key,invalid_api_key,insufficient_scope,channel_disabled - Validação de campos:
missing_type,invalid_document,document_type_mismatch,missing_name,invalid_email,invalid_birth_date,invalid_address,invalid_external_id,invalid_metadata,invalid_webhook_url, einvalid_<campo>para cada campo complementar (nationality,occupation,declared_income,trade_name,annual_revenue,business_activity,employee_count) - Bureau (
run_bureau: true):missing_default_template,template_not_found,subject_type_mismatch - Conflito:
customer_already_exists,external_id_conflict— ver Cliente duplicado (409);idempotency_key_in_progress— ver HeaderIdempotency-Key
Authorizations
API key no header x-api-key (recomendado)
Headers
UUID único por tentativa. Mesmo valor nas próximas 24h retorna a resposta
original sem criar o cliente nem disparar o bureau de novo — use sempre
que implementar retry no seu código, especialmente com run_bureau: true.
"2026-06-03"
Body
Tipo do cliente — pessoa física ou jurídica
pf, pj CPF (11 dígitos) ou CNPJ (14 dígitos), sem formatação
"12345678901"
Nome completo (PF) ou razão social (PJ)
2 - 300Email do cliente. Obrigatório para PF.
Telefone do cliente. Opcional.
Data de nascimento no formato YYYY-MM-DD (apenas PF)
Nacionalidade (apenas PF). Deve ser um valor exato da lista canônica usada pelo seletor do portal público de cadastro.
"Brasileira"
Profissão declarada (apenas PF).
120"Engenheiro(a) de Software"
Renda mensal declarada (apenas PF). Aceita um valor decimal (ex: "12000.00") ou um código de faixa (ex: "10001_20000", mesmo formato gravado quando o tenant usa modo de faixa no portal) — validado apenas como string não vazia, sem normalizar o formato. Apenas armazenado: não aciona recálculo automático de capacidade financeira (motor de renda).
20"12000.00"
Endereço do cliente. Todos os subcampos são opcionais entre si — envie apenas os que tiver. Chaves fora das listadas abaixo são aceitas e persistidas, mas ignoradas na validação.
Show child attributes
Show child attributes
Nome fantasia (apenas PJ).
300"Kycert Tech"
Faturamento anual declarado (apenas PJ). Mesma regra de
passthrough de declared_income — string não vazia, sem
normalizar o formato. Apenas armazenado: não aciona recálculo
automático de capacidade financeira (motor de renda).
20"5000000.00"
Atividade principal da empresa (apenas PJ). Texto livre — não é validado contra nenhuma lista curada de atividades.
120"Desenvolvimento de software / TI"
Número de funcionários declarado (apenas PJ).
10"25"
Seu identificador interno para este cliente. Deve ser único no tenant.
255Até 10 pares chave-valor string
Show child attributes
Show child attributes
Se true, dispara um bureau run imediatamente após criar o cliente.
Não requer template_id quando há um template padrão configurado
para o tipo de pessoa (type) em Configurações → Bureau no
dashboard — nesse caso a resolução é automática (ver template_id
e onboarding_profile abaixo). Sem template_id explícito e sem
template padrão configurado, retorna 400 missing_default_template.
ID do template de bureau. Opcional quando run_bureau: true e
houver um template padrão configurado no dashboard para o type
do cliente (bureau_default_template_pf_id/_pj_id) — nesse caso
é resolvido automaticamente, com prioridade: template_id
explícito > onboarding_profile > default do tenant. Obrigatório
apenas se nenhum template padrão estiver configurado.
Slug de um perfil de cadastro (o mesmo usado em ?perfil=slug no
portal público) — quando enviado e válido, o template configurado
nesse perfil tem prioridade sobre o default do tenant na resolução
automática de template_id. Slug inexistente, inativo, ou de
outro tenant é ignorado silenciosamente (cai no default do
tenant) — nunca gera erro. Só tem efeito quando template_id não
é enviado explicitamente.
"cadastro-pf-completo"
URL HTTPS para entrega do resultado do bureau (quando run_bureau: true)
Response
Cliente criado com sucesso
Identificador único do cliente
customer em_analise, aprovado, pendencia, recusado "em_analise"
pf, pj CPF/CNPJ mascarado — nunca retorna em claro
"***456789**"
ID do run de bureau criado (presente apenas quando run_bureau=true)
Status do bureau (presente apenas quando run_bureau=true)
queued