Skip to main content
A consulta/cotação e a formalização desta API estão implementadas e disponíveis: as rotas /partners/v1/* — incluindo a submissão de dados complementares, o link de assinatura e o acompanhamento de status descritos em Formalizando uma proposta — já estão no ar. O ato de assinar continua acontecendo no fluxo regulado do próprio banco (ZipSign, biometria, DATAPREV); a API entrega o link para o cliente completar. Para obter credenciais (API key e configuração de webhook), fale com o time de parceiros da Unit.

Visão geral

Sua aplicação já coleta os dados do cliente e o consentimento dele. Em vez de integrar banco a banco, você faz uma única chamada para a Unit com os dados do titular e a evidência de autorização — a Unit decide, banco a banco, como rotear: Você recebe, numa resposta só, o resultado de cada banco: as ofertas dos que aceitaram a evidência direto, e as ações pendentes (link de biometria, DATAPREV, etc.) dos que exigem uma autorização externa do titular — mais o erro ou o motivo, por banco, quando não houver oferta.

Como isso se encaixa no seu fluxo

Você não precisa mudar a experiência do seu produto — a API de Parceiros entra depois que você já tem os dados e o consentimento do cliente:
1

Colete os dados do titular

CPF, nome completo, data de nascimento, telefone e e-mail — o que você já coleta hoje no seu onboarding.
2

Colete o consentimento e a evidência

Apresente o termo de autorização para consulta de margem/vínculo no seu produto e registre a evidência do aceite: IP do titular, geolocalização e o momento do aceite.
3

Chame a API de Parceiros

Envie os dados e a evidência para uma das rotas de consulta (abaixo). Numa chamada só você consulta todos os bancos habilitados para a sua parceria — ou um subconjunto, ou um banco específico.
4

Trate a resposta

Mostre as ofertas retornadas. Para bancos que exigem autorização externa, apresente o link de ação para o cliente completar (ex.: biometria).
5

Receba atualizações assíncronas (quando aplicável)

Bancos assíncronos, ou autorizações externas concluídas depois da resposta inicial, chegam via webhook — não é preciso ficar consultando o status.

Autenticação

Toda chamada usa uma API key exclusiva do seu parceiro, enviada no header:
A Unit resolve a sua parceria a partir dessa key. Requisições sem a key, com key inválida ou com a parceria suspensa recebem 401.
Trate sua API key como um segredo — nunca a exponha em código client-side/frontend. Todas as chamadas devem partir do seu backend.

Bancos habilitados

Quais bancos a sua integração pode consultar é definido em duas camadas:
  1. Allowlist da parceria (fixo, no servidor). Cada parceria tem uma lista de bancos permitidos, definida no seu contrato. A Unit aplica esse limite sempre — uma chamada nunca alcança um banco fora do seu allowlist.
  2. Seleção por chamada (opcional, dentro do allowlist). Em cada requisição você pode restringir ainda mais quais bancos consultar, com o campo providers (veja abaixo). Ausente, a consulta usa todo o seu allowlist.
A cobertura de bancos disponível para a sua integração depende do seu contrato de parceria — confirme com o time de parceiros da Unit quais bancos estão habilitados para você.

Enviando uma consulta

Há três formas de consultar, todas com o mesmo corpo de requisição:

Corpo da requisição

string
required
CPF do titular, apenas dígitos.
string
required
Nome completo do titular.
string
required
Data de nascimento no formato YYYY-MM-DD.
string
required
Telefone do titular no formato DDD+número, 10-11 dígitos (ex.: 11999998888). O DDI 55 é opcional — se vier (5511999998888), a API remove automaticamente. Qualquer outro formato retorna 422 invalid_input.
string
required
E-mail do titular, em formato válido. Vazio ou inválido retorna 422 invalid_input — o HubCrédito não processa consultas sem e-mail.
string
required
IP do titular no momento do aceite — parte da evidência de autorização. Como sua chamada é feita pelo seu backend, este campo precisa vir explícito: a Unit não tem como inferir o IP do seu cliente a partir da sua infraestrutura.
object
Geolocalização do titular no momento do aceite — { "lat": number, "lng": number }. Recomendado; alguns bancos usam esse dado como parte da evidência.
string[]
Restringe esta chamada a um subconjunto dos bancos (ex.: ["unit", "c6"]). O resultado é sempre a interseção com o allowlist da sua parceria — pedir um banco fora do allowlist não o inclui. Ausente, consulta todo o allowlist. (Ignorado na rota individual, que já mira um banco pelo path.)Desde 13/08: se nenhum nome pedido estiver no seu allowlist (ex.: typo "pan" em vez de "banco_pan"), a chamada devolve 422 invalid_input (field: "providers") listando os bancos disponíveis do seu contrato — em vez de responder 200 com providerResults vazio.
integer
default:"0"
Sexo do titular, quando exigido por algum banco na simulação.
object
Bloco opcional para elevar a força da evidência em bancos que aceitam autorização “tipo app”: authorizationId, signatureDate e um objeto evidence com userAgent, operationalSystem, deviceModel, deviceName, deviceType e geoLocation. Use se você já capturar esses dados de device na sua própria integração.
string
Identificador de originação/atribuição, quando aplicável ao seu contrato. Na maioria dos casos, não envie: a consulta usa a carteira padrão de cada banco (na Unit, com fallback automático de carteira quando não há vínculo elegível).
number
Modo “por valor final”: simula o valor de empréstimo desejado. Envie junto com prazo; se prazo for omitido, a Unit assume 48 parcelas (o mesmo default do nosso formulário) e ajusta automaticamente quando o prazo não está disponível para o cliente. Opcional.
integer
Número de parcelas desejado. Acompanha loan_value (por valor) ou valor_parcela (por parcela). Opcional — default 48 no modo por valor.
number
Modo “por parcela”: simula pela parcela mensal desejada — o valor é derivado automaticamente. É o modo universal (todos os bancos simulam por parcela). Envie junto com prazo. Opcional. Quando loan_value e valor_parcela vêm juntos, o valor tem prioridade.
boolean
default:"true"
Inclui as opções com seguro na simulação.

Resposta — consulta agregada

A rota agregada devolve, numa resposta só, as ofertas achatadas (ordenadas por valor) e o resultado detalhado por banco:
array
Todas as ofertas de todos os bancos, achatadas e ordenadas por valor. Cada oferta traz provider, loanValue, installmentValue, installments, interestRate, totalAmount e detalhes do vínculo (employerName, employerCnpj, matricula), além de providerData com campos específicos do banco.Cada oferta também traz adjusted (bool) e adjustments (array de strings legíveis): quando os parâmetros pedidos (prazo, loan_value, valor_parcela) não puderam ser atendidos exatamente, a oferta volta com o melhor possível e o ajuste sinalizado — ex. "prazo solicitado (36x) indisponível — melhor prazo: 18x" ou "valor solicitado acima do máximo disponível". Use-os para não apresentar a simulação como se fosse exatamente a pedida.
object
Resultado detalhado por banco, indexado pelo nome do banco. É onde você lê o estado de cada um:
  • Ofertas do bancoproposals.
  • Ação pendente (autorização externa) → requiresSignature: true com signatureUrl, e/ou noProposalReasons explicando o que falta.
  • Erro / recusasuccess: false com error.
array
Empregadores únicos (por CNPJ) encontrados para o titular: {cnpj, name, matricula, provider, margin}. margin é a margem consignável disponível, apurada em tempo real na consulta, normalizada em número (float) a partir do que o banco do vínculo informa (Unit e Facta trazem hoje); vem null quando o banco não informa margem. Não confundir com o consigned_credit_balance do lookup de leilão, que é o retrato no momento em que o convite do leilão foi gerado.
integer
Tempo total da consulta agregada, em milissegundos.
string | null
Identificador do grupo de simulação, quando a consulta persiste as ofertas.

Resposta — streaming (SSE)

A rota /consultas/stream responde text/event-stream: cada evento é uma linha data: {json}\n\n com uma chave event. Os bancos chegam à medida que respondem, sem esperar o mais lento:
O streaming entrega os resultados progressivos por banco, mas não dispara os efeitos cross-provider da rota síncrona (webhooks de lead, persistência das ofertas). Se você depende desses efeitos, use a rota agregada síncrona (POST /partners/v1/consultas).

Resposta — consulta individual

A rota individual devolve um único envelope do banco consultado (campos em snake_case):
string
Estado normalizado do banco: ok (com oferta), no_offers (sem oferta), requires_action (precisa de autorização externa), pending (fluxo assíncrono em andamento) ou error.
array
Ofertas deste banco.
object | null
Ação pendente quando status: requires_actiontype (signature | biometria | dataprev | whatsapp_link | formalization), url (o link para o cliente completar) e detail.
string | null
Motivo, quando o banco falhou ou recusou.
Pedir um banco fora do seu allowlist retorna 403 (não 404 — a API não revela se o banco existe). Um nome de banco desconhecido retorna 404.

Fluxo de leilão — lookup por proposal_key

Quando o cliente chega até você pelo fluxo de leilão (link com ?proposal_key=... na URL), você pode recuperar a proposta do leilão e os dados do cliente antes de pedir qualquer coisa na tela — e pré-preencher a jornada inteira:
Sem efeitos colaterais: a consulta não cria proposta, não dispara SMS e não consome limites do cliente. Chame quantas vezes precisar.
Resposta (200):
Semântica dos campos — três esclarecimentos importantes:
  • disbursed_issue_amount na raiz = valor pedido pelo trabalhador no leilão da CTPS. proposal.proposal_data.disbursed_issue_amount = valor da proposta enviada (o lance real). Apresente ao cliente sempre o de proposal_data, usando proposal_data.simulation para parcela/CET; o da raiz serve para contexto (“você pediu X, conseguimos Y”).
  • consigned_credit_balance = margem disponível no momento em que o convite do leilão foi gerado — pode estar defasada. A margem em tempo real é a employers[].margin da consulta. Para régua de elegibilidade, use este campo como pré-filtro barato e a margem da consulta como critério final.
  • Campos de repasse (webhook_type, alerts, rank_position, event_datetime, inclusion_limit_datetime, external_proposal_url e as chaves internas *_key): vêm da instituição como estão, podem vir null (o rank_position inclusive em produção) e podem mudar sem aviso — não construa lógica obrigatória sobre eles. O que tem prazo é o lance (expiration_datetime); a chave em si continua resolvendo depois disso.
A proposal_key é uma capability: opaca, não-enumerável, e chega até você pelo próprio link do leilão. A expiração do convite do leilão não invalida a chave — dias depois ela ainda resolve, o que permite retomar uma jornada abandonada.
No sandbox, use as chaves de teste sbx-leilao-ok (retorna 200 com um cliente fictício — CPF 11144477735, o mesmo do catálogo de cenários, então você encadeia lookup → consulta → formalização com um CPF só) e sbx-leilao-expirada (retorna 404, para testar seu tratamento de erro).

Modelo de autorização por banco

Nem todos os bancos aceitam a evidência da mesma forma. Alguns autorizam a consulta direto com os dados enviados na chamada; outros exigem uma ação externa do titular:
Banco PAN ainda não faz parte do fluxo automatizado da API de Parceiros — está em desenvolvimento. Ele aparece nesta tabela pelo modelo de autorização, mas hoje não retorna oferta nem ação pendente por esta API.
C6 — a biometria vem antes da oferta. Diferente dos outros bancos, o C6 só gera oferta depois que o titular conclui a prova de vida. Enquanto isso, a consulta retorna o C6 sem oferta, com o link de biometria em action (type: "biometria"). Quando o cliente conclui:
  1. a Unit envia o webhook authorization_result com as ofertas do C6; e
  2. as ofertas também passam a aparecer numa nova consulta do mesmo CPF (fallback caso você não use webhook).
Só então siga para a formalização. O GET /partners/v1/proposals/{id} não se aplica nessa espera — ainda não existe proposta (o submit vem depois da oferta).

Autorização com colheita de evidência (fluxo WhatsApp)

Atendimentos 100% WhatsApp não têm como colher a evidência de autorização que Zipdin, Unit e Facta exigem (IP do titular, geolocalização, dispositivo). Para esses casos, a API gera um link de autorização: o cliente abre uma página compacta, lê o termo LGPD e autoriza com um toque — a evidência é colhida ali (IP real capturado no servidor, geolocalização, dispositivo, versão do termo) e fica registrada na Unit como fonte da verdade.
1

Crie a sessão

POST /partners/v1/authorizations com {cpf, nome, telefone}{authorization_id, url, expires_at} (validade 72h). Idempotente: sessão pendente viva do mesmo CPF devolve o mesmo link.
2

Envie o link na conversa

O cliente abre a url, vê o termo com o nome dele e CPF mascarado, e autoriza com um toque. Localização negada não bloqueia (fica registrada como não-colhida).
3

Receba o sinal

Webhook authorization.completed no seu webhook_url (mesma assinatura HMAC do authorization_result) — ou poll em GET /partners/v1/authorizations/{authorization_id}, que devolve o status e, quando autorizada, a evidence completa:
4

Consulte com o authorization_id

Nas rotas de consulta, envie authorization_id no lugar de client_ip: a evidência colhida é injetada no servidor — sua integração nunca transita IP/geo do cliente.
No sandbox o fluxo inteiro é testável por API: as rotas public/* são abertas, então o passo do cliente pode ser simulado chamando POST /partners/v1/authorizations/public/{authorization_id}/accept diretamente com uma evidência de teste. A collection do Postman tem a pasta “Autorização — colheita de evidência” com os 5 passos prontos.

Formalizando uma proposta

Depois que o cliente escolhe uma oferta, a operação ainda precisa ser formalizada com o banco — e isso exige um segundo conjunto de dados, que a consulta de evidência (acima) não cobre: endereço completo, dados bancários ou chave PIX para o desembolso, documento de identidade, estado civil e dados de emprego e renda. A API de Parceiros cobre também essa etapa — submissão dos dados complementares, link de assinatura e acompanhamento de status — nas mesmas rotas /partners/v1/proposals*.
1

Escolha uma oferta

Use o provider e o identificador da oferta retornados na consulta (ver offer_ref abaixo).
2

Submeta os dados complementares

POST /proposals com a oferta escolhida (offer_ref) e o dataset de submissão (endereço, documento, dados bancários/PIX etc.). A resposta traz o proposal_id — nosso identificador estável para o restante do fluxo.
3

Peça o link de assinatura

POST /proposals/{proposal_id}/formalize devolve action.url: o link para o cliente completar a assinatura no fluxo do banco (ZipSign, biometria, DATAPREV etc.).
4

Acompanhe o status

GET /proposals/{proposal_id} devolve o status normalizado a qualquer momento, sem que você precise reimplementar o fluxo de cada banco.

Rotas

O ato de assinar acontece no fluxo regulado do banco (ZipSign, biometria C6, DATAPREV, Unit-sign) — a API de Parceiros entrega o link e o status; ela não captura a assinatura.

Dataset de submissão

POST /proposals recebe um objeto único, o mesmo para todos os bancos. A API valida o subconjunto exigido pelo banco-alvo e devolve 422 com o(s) campo(s) faltando, antes de qualquer chamada ao banco.
object
required
A oferta escolhida — { provider, offer_id }. Envie o offerId que veio em cada oferta da consulta — ele identifica a oferta persistida e é resolvido server-side para os quatro bancos: a API recupera os identificadores e termos reais da oferta (na Facta, a simulação é re-executada no momento do submit — o identificador nunca expira; na Zipdin, a proposta do pré-registro é localizada automaticamente). Compat: os identificadores nativos do providerData continuam aceitos.
object
required
Dados do titular: nome, cpf, data_nascimento, sexo ("M" ou "F" — normalizado; a API traduz para o código de cada banco), nome_mae, e, conforme o banco, nome_pai, nacionalidade, estado_civil, naturalidade_cidade, naturalidade_uf, exposicao_politica. nacionalidade e naturalidade_cidade vão por extenso (“Brasileira”, “São Paulo”) — a API traduz para o código exigido por cada banco. estado_civil: envie a palavra em portuguêssolteiro, casado, divorciado, viuvo, separado ou uniao_estavel; a API traduz para o código/formato de cada banco (não envie códigos numéricos — a numeração varia por banco).
object
Documento de identidade (RG): numero, orgao_emissor, uf_emissor, data_emissao. Exigido pela maioria dos bancos — ver matriz abaixo.
object
required
telefone e email do titular.
object
required
Endereço completo: cep, logradouro, numero, complemento (opcional), bairro, cidade, uf.
object
Dados de emprego e renda: cnpj_empregador, nome_empregador, cargo, salario, margem, data_admissao, matricula. Exigência varia por banco — ver matriz abaixo.
object
required
Para onde o crédito é desembolsado — exatamente um dos dois: conta_bancaria (banco, agencia, digito_agencia opcional, conta, digito_conta, tipo_conta) ou pix (tipo_chave, chave). Enviar os dois, ou nenhum, retorna 422. O campo banco usa o código COMPE — consulte a lista aceita em GET /partners/v1/banks.
  • tipo_conta: corrente, poupanca ou salario (a API traduz para o enum de cada banco). Máscaras em agência/conta/CEP são removidas automaticamente.
  • pix.tipo_chave: cpf, telefone, email ou aleatoria. Chave de CPF/celular é normalizada para o formato do banco. Zipdin não aceita PIX por telefone — nesse caso o desembolso vai para a chave PIX-CPF do titular.
object
required
ip — IP do titular no momento do aceite. tipo_envio ("sms" ou "whatsapp") controla como o link de assinatura é entregue, quando o banco suportar.

Matriz de exigência por banco

Cada bloco é obrigatório, opcional ou irrelevante conforme o banco. Campos faltando para o banco-alvo retornam 422 com o campo, antes de qualquer chamada externa. ¹ Na Unit, PIX é bloqueado para propostas roteadas para a carteira BMP — a validação recusa pix nesse caso, com mensagem clara. ² Na Zipdin, vinculo cobre apenas cargo/empregador — não inclui renda. ³ A Crefaz exige 2 referências pessoais (nome + telefone; grau: 0=cônjuge, 1=parente, 2=amigo — default parente). Envie no bloco referencias do dataset (os demais bancos ignoram o bloco).
Banco PAN — fluxo assíncrono (autorização por WhatsApp). A consulta devolve uma ação pendente com o link de WhatsApp do banco — o cliente precisa enviar a mensagem para autorizar. Enquanto isso, um submit retorna 422 invalid_input apontando offer_ref.offer_id (ainda não existe oferta precificada para submeter — não é um erro retryable de infraestrutura). Concluída a autorização (o webhook authorization_result avisa), a próxima consulta devolve as ofertas precificadas com offerId — daí em diante o fluxo é o padrão (submit → status → formalize com link digital).
Crefaz — fluxo assíncrono (autorização por SMS). A consulta dispara um SMS DataPrev pro cliente; enquanto ele não autorizar, a consulta volta com ação pendente e um submit retorna 422 invalid_input apontando offer_ref.offer_id — aguarde o webhook authorization_result (ou re-consulte) e submeta com o offerId novo. Depois da Mesa aprovar, o formalize entrega o link de assinatura (Unico) e o status reporta awaiting_signature.

Envelope de resposta

submit, formalize e status devolvem o mesmo envelope — você escreve um handler de status e um de erro, independente do banco:
string
Nosso identificador opaco e estável da proposta — use-o em formalize, status e cancel.
string
Estado normalizado da proposta (enum abaixo).
object | null
Link de assinatura quando status: awaiting_signaturetype: "signature", url (o link para o cliente completar) e delivery.
object | null
A oferta associada à proposta (loanValue, installmentValue, installments, interestRate, …).
object | null
Erro normalizado, quando houver — code, message, retryable e field (ver tabela de códigos abaixo).
object | null
Extensão crua por banco: identificadores e status originais, para quem precisar do detalhe.

Status normalizado

Códigos de erro

O contrato de erro é normalizado — um handler para o parceiro, não um por banco. error.code é estável entre bancos; retryable indica se vale a pena tentar de novo; field aponta o campo, quando aplicável.

Segurança

proposal_id é escopado ao seu parceiro: só quem submeteu a proposta pode formalizá-la, consultá-la ou cancelá-la. Tentar acessar a proposta de outro parceiro (ou fora da sua allowlist de bancos) retorna 404.

Exemplo

Resposta de formalize, com o link de assinatura pronto:

Lista de bancos

GET /partners/v1/banks (autenticado pela mesma API key) devolve as instituições bancárias aceitas em recebimento.conta_bancaria.banco:
É uma lista curada de referência para o seu formulário — a aceitação final de cada instituição é validada pelo banco de crédito no submit/formalização.

Sandbox de Homologação

Antes de integrar em produção, teste sua integração no sandbox — um ambiente que responde exatamente como a API real (mesmas rotas, mesmo contrato, mesmos códigos de erro), mas 100% simulado: não bate em nenhum banco de verdade e não usa dados reais.
Base URL do sandbox: https://sandbox.api.somosunit.com.br. Chave de teste fixa (pública, pode usar livremente): au_sandbox00000000000000000000000.

CPFs de teste — catálogo de cenários

Re-simulação no sandbox (14/08): prazo, loan_value e valor_parcela alteram os números das ofertas, como em produção. O cenário de cada CPF é o teto (valor = margem máxima, parcelas = prazo máximo): pedido acima do teto volta clampado, com o ajuste sinalizado em adjusted/adjustments. Os números seguem um modelo linear simplificado (sem juros compostos) — use para homologar o fluxo de re-simulação e teto, não para validar precificação.
Além dos CPFs, o fluxo de leilão tem chaves de teste próprias (GET /auction-proposals/{key}): sbx-leilao-ok200 e sbx-leilao-expirada404 — ver Fluxo de leilão.
Use POST /partners/v1/consultas (agregada) antes do submit — não a rota individual. Só a agregada persiste as ofertas e devolve o offerId que o submit usa (offer_ref.offer_id), igual em produção. O offerId vem preenchido em todas as respostas da consulta agregada — inclusive em re-consultas imediatas (a mesma oferta mantém o mesmo offerId entre consultas; ofertas novas ganham id próprio). Use sempre o offerId — o simulationId do providerData muda a cada consulta na Unit.

Collection do Postman

Baixe a collection com todos os endpoints (consulta e formalização) e os dois environments prontos (produção e sandbox): Importe os 3 arquivos no Postman, selecione o environment desejado e preencha apiKey (produção) — no sandbox a chave já vem preenchida.

Webhooks

Para bancos assíncronos e para autorizações externas que se completam depois da resposta inicial (ex.: C6 após a biometria), a Unit notifica sua aplicação via webhook, na URL configurada no cadastro do seu parceiro.
A configuração de webhook (URL + secret) já faz parte do cadastro do parceiro. O evento authorization_result do C6 (pós-biometria) já está no ar: quando o cliente conclui a prova de vida, a Unit envia o webhook com as ofertas do C6. O Banco PAN segue a mesma forma quando entrar no fluxo automatizado.

Payload

Verificando a assinatura

Toda chamada de webhook inclui um header X-Unit-Signature com um HMAC-SHA256 do corpo da requisição, assinado com o webhook secret do seu cadastro. Valide a assinatura antes de processar o payload:
Sempre compare assinaturas com uma função de comparação em tempo constante (timingSafeEqual/compare_digest) — nunca com === ou ==, que vazam tempo de execução e enfraquecem a proteção.
Seu endpoint deve responder 2xx para confirmar o recebimento. Chamadas sem confirmação são reenviadas com backoff.
A entrega é at-least-once: em cenários raros (uma falha de rede entre a gravação da oferta e o envio), o mesmo evento pode chegar mais de uma vez. Trate os webhooks de forma idempotente — deduplique pela combinação (external_reference, event).
Dúvidas sobre a integração? Fale com o time de parceiros da Unit.