Onboarding Banking

Fluxo passo a passo de abertura de conta digital — atende PF (CPF) e PJ (CNPJ + sócios), com integração Dock, KYC via Unico SDK, convite multicanal para sócios e webhooks de progresso.

Este guia explica exatamente como abrir uma conta digital na Autra, chamada por chamada. A API atende dois tipos de pessoa:

  • PF — pessoa física (CPF). Quem está abrindo conta no próprio nome.
  • PJ — pessoa jurídica (CNPJ + sócios). Empresas, MEIs, etc.

Os dois fluxos compartilham a maior parte dos endpoints. Só os passos que dependem do tipo (preencher CPF x preencher CNPJ + sócios, e o disparo do processo) são diferentes.

Provedor BaaS: Dock. Você não fala com a Dock — só com a API da Autra. Nós orquestramos.


Antes de começar — checklist

  • Você tem client_id + client_secret da Autra.
  • Você sabe gerar um Bearer JWT via POST /v1/oauth/token?grant_type=client_credentials (rota Authentication, header Authorization: Basic base64(client_id:client_secret)).
  • Você decidiu se vai abrir conta PF ou PJ.
  • Frontend: você tem device_id (UUID) do dispositivo que vai fazer o KYC.
  • Frontend: você integrou (ou vai integrar) o SDK Unico para captura da selfie + documento de identificação (RG / CIN / CNH / RNE / Passaporte).
  • (PJ) Você sabe quem é o master — sócio responsável pelo onboarding.
  • (PJ) Você sabe quem são os outros sócios e como vai contatá-los (email/WhatsApp).

Resumo dos endpoints — PF x PJ

#EtapaPFPJ
1Criar propostaPOST /onboardingPOST /onboarding
2Buscar termosGET /onboarding/{id}/termsGET /onboarding/{id}/terms
3Aceitar termosPOST /onboarding/{id}/terms/acceptPOST /onboarding/{id}/terms/accept
4Salvar dadosPUT /onboarding/{id}/personal-dataPUT /onboarding/{id}/legal-entity
5Sócios— (não tem)POST /onboarding/{id}/partners (1+ vezes)
6Convidar sóciosPOST /onboarding/{id}/partners/{partnerId}/invite
7Sócio convidado preenchePUT /onboarding/invite/{token}/personal-data (público)
8Iniciar processo DockPOST /onboarding/{id}/startPOST /onboarding/{id}/start-pj
9Pollar arquivos pendentesGET /onboarding/{id}/files/pendingGET /onboarding/{id}/files/pending
10Upload selfie + doc identificaçãoPOST /onboarding/{id}/filesPOST /onboarding/{id}/files/pj/partners/{partnerId}
11Upload docs empresaPOST /onboarding/{id}/files/pj/company
12Marcar uploads completosPOST /onboarding/{id}/files/completePOST /onboarding/{id}/files/complete
13Pollar status finalGET /onboarding/{id}GET /onboarding/{id}

Todas as rotas usam o prefixo https://api.autra.io/v1/banking. Headers obrigatórios em rotas privadas: Authorization: Bearer <JWT>, Content-Type: application/json.


State machine (estados da proposta)

A proposta passa por estados. Você só avança chamando o endpoint certo no estado certo. Se errar, recebe 409 INVALID_STATUS.

DRAFT
 └─► TERMS_ACCEPTED
       │
       │  (PF)
       ├─► PERSONAL_DATA_FILLED
       │     └─► PROCESS_STARTED
       │           └─► FILES_PENDING
       │                 └─► FILES_UPLOADED
       │                       └─► PROCESSING
       │                             └─► COMPLETED | DECLINED | WAITING_*
       │
       │  (PJ)
       └─► LEGAL_ENTITY_FILLED
             └─► PARTNERS_PENDING       (algum sócio incompleto/INVITED)
                   └─► PARTNERS_READY   (todos sócios READY + 1 master)
                         └─► PROCESS_STARTED
                               └─► FILES_PENDING
                                     └─► FILES_UPLOADED
                                           └─► PROCESSING
                                                 └─► COMPLETED | DECLINED | WAITING_*

  Estados terminais: COMPLETED · DECLINED · CANCELED · FAILED

Regra de ouro: o backend é fonte de verdade. Sempre cheque o status na resposta antes de chamar o próximo endpoint.


🟦 Fluxo PF — passo a passo

PF — Passo 1: Criar a proposta

POST /v1/banking/onboarding
Authorization: Bearer <JWT>
Content-Type: application/json
Idempotency-Key: pf-jorge-2026-05-01

{ "type": "PF" }

Resposta 201:

{ "id": "0193b5a1-...", "type": "PF", "status": "DRAFT", ... }

Guarde o id — você vai usar em todas as chamadas seguintes. ✅ Idempotency-Key garante que se a chamada falhar e você reenviar com a mesma chave, a mesma proposta volta (não cria duplicada).


PF — Passo 2: Buscar os termos vigentes

GET /v1/banking/onboarding/{id}/terms
Authorization: Bearer <JWT>

Resposta 200:

{
  "tou":          { "token": "tou-abc...", "title": "Termos de Uso", "url": "https://..." },
  "privacy":      { "token": "pp-xyz...",  "title": "Política de Privacidade", "url": "https://..." }
}

✅ Mostre os textos para o usuário ler. Guarde os 2 tokens (tou.token e privacy.token).


PF — Passo 3: Aceitar os termos

POST /v1/banking/onboarding/{id}/terms/accept
Authorization: Bearer <JWT>

{
  "tou_token": "tou-abc...",
  "pp_token":  "pp-xyz...",
  "fingerprint": "Mozilla/5.0 (iPhone)#187.45.32.10"
}

Resposta 200: status: TERMS_ACCEPTED.

✅ O fingerprint é uma string livre — recomendamos <UserAgent>#<IP> para auditoria.


PF — Passo 4: Salvar dados pessoais

PUT /v1/banking/onboarding/{id}/personal-data
Authorization: Bearer <JWT>

{
  "personal_data": {
    "full_name":   "Jorge Augusto Silva",
    "cpf":         "39053344705",
    "birth_date":  "1985-03-10",
    "mother_name": "Maria da Silva",
    "email":       "[email protected]",
    "phone":       { "area_code": "11", "number": "914052149" }
  },
  "address": {
    "zip_code":     "06404250",
    "suffix":       "Avenida",
    "street":       "Anápolis",
    "number":       "100",
    "neighborhood": "Alphaville",
    "city":         "Barueri",
    "state":        "SP"
  }
}

Resposta 200: status: PERSONAL_DATA_FILLED.

⚠️ Validamos: CPF (check digits), idade ≥ 18, CEP, formato de email.


PF — Passo 5: Iniciar processo na Dock

POST /v1/banking/onboarding/{id}/start
Authorization: Bearer <JWT>

{
  "device_id":        "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "device_alias":     "iPhone do Jorge",
  "device_model":     "iPhone 15",
  "operating_system": "iOS 18",
  "ip_address":       "187.45.32.10"
}

Resposta 200:

{
  "status": "PROCESS_STARTED",
  "process_instance_id": "abc-123",
  "dock_proposal_id":    "def-456",
  "sdk_key": "eyJhbGciOi..."
}

Guarde o sdk_key — é o token efêmero do Unico SDK que você vai usar para capturar a selfie no app.

⚠️ A partir daqui, a Dock está processando. Não chame /start de novo (é idempotente, mas é desnecessário). Vá pro passo 6.


PF — Passo 6: Esperar liberação para upload (polling)

A Dock leva alguns segundos para liberar o estágio de upload. Pollar até receber 200:

GET /v1/banking/onboarding/{id}/files/pending
Authorization: Bearer <JWT>
  • 202 → ainda processando. Aguarde ~2s e tente de novo.
  • 200 → liberado. Resposta:
{
  "proposal": { ... },
  "files": [
    { "file_side": "SELFIE" },
    { "file_side": "IDENTITY_CARD_FRONT" },
    { "file_side": "IDENTITY_CARD_VERSE" }
  ],
  "groups": [
    {
      "file_type": "BIOMETRIC_FILE",
      "options": [
        { "category": "SELFIE", "label": "Selfie", "sides": ["SELFIE"] }
      ]
    },
    {
      "file_type": "IDENTIFICATION_FILE",
      "options": [
        { "category": "IDENTITY_CARD",          "label": "RG",          "sides": ["IDENTITY_CARD_FRONT", "IDENTITY_CARD_VERSE"] },
        { "category": "DRIVER_LICENSE",         "label": "CNH",         "sides": ["DRIVER_LICENSE_FRONT", "DRIVER_LICENSE_VERSE"] },
        { "category": "DIGITAL_DRIVER_LICENSE", "label": "CNH Digital", "sides": ["DIGITAL_DRIVER_LICENSE"] },
        { "category": "RNE",                    "label": "RNE",         "sides": ["RNE_FRONT", "RNE_VERSE"] },
        { "category": "PASSPORT",               "label": "Passaporte",  "sides": ["PASSPORT"] }
      ]
    }
  ]
}

Como o app escolhe o tipo de documento:

  • groups[].file_type = "BIOMETRIC_FILE" é sempre obrigatório (1 opção: SELFIE).
  • groups[].file_type = "IDENTIFICATION_FILE" traz N opções — o app deve mostrar pro usuário uma escolha (RG, CIN, CNH, CNH Digital, RNE, Passaporte) e enviar todos os sides da opção escolhida via POST /files.
  • O campo legacy files[] continua existindo, mas pega só a 1ª opção (sempre RG). Use groups[] no app moderno pra dar a escolha pro usuário.

CIN (Carteira de Identidade Nacional, novo modelo 2022) entra na mesma categoria que o RG: IDENTITY_CARD com sides IDENTITY_CARD_FRONT + IDENTITY_CARD_VERSE. A Dock não diferencia. No app, você pode mostrar "CIN" e "RG" como opções separadas (UX) e enviar usando os mesmos sides.

✅ Use backoff exponencial: 2s → 4s → 8s, máx 30s entre tentativas. Total: até 2min.

Dica: se você assina o webhook process_files_receives_started, evita polling. O backend te avisa.


PF — Passo 7: Upload da selfie e do documento de identificação

Para cada file_side da opção escolhida no passo 6 (mais a selfie):

POST /v1/banking/onboarding/{id}/files
Authorization: Bearer <JWT>

{
  "file_side": "SELFIE",
  "jwt":       "<JWT do Unico SDK obtido no app>"
}
POST /v1/banking/onboarding/{id}/files
Authorization: Bearer <JWT>

{
  "file_side": "IDENTITY_CARD_FRONT",
  "base64":    "<base64 da imagem capturada pelo SDK Unico>",
  "jwt":       "<JWT do Unico SDK>"
}

Mapeamento file_side por tipo de documento (extraído do groups[] do passo 6):

Documentofile_side (uma chamada por lado)
SelfieSELFIE (sempre obrigatório, vai em jwt)
RG / CINIDENTITY_CARD_FRONT + IDENTITY_CARD_VERSE
CNH físicaDRIVER_LICENSE_FRONT + DRIVER_LICENSE_VERSE
CNH digitalDIGITAL_DRIVER_LICENSE (1 arquivo único — PDF/JPG do app oficial)
RNERNE_FRONT + RNE_VERSE
PassaportePASSPORT (foto da página de identificação)

Regras importantes:

  • Selfie vai no campo jwt (vem direto do SDK Unico, é JWT que carrega a imagem + liveness).
  • Documentos vão no campo base64 (mais o jwt do SDK quando capturados via Unico). Para CNH digital exportada do app, é o conteúdo do arquivo em base64.
  • Frente e verso são chamadas separadas — uma POST /files por lado.
  • O backend repassa o JWT/base64 pra Dock; nada é validado/transformado no nosso lado.

PF — Passo 8: Marcar uploads como completos

POST /v1/banking/onboarding/{id}/files/complete
Authorization: Bearer <JWT>

Resposta 200: status: FILES_UPLOADED.

⚠️ Pré-condição: todos os arquivos do passo 6 enviados. Se faltar, retorna 409 FILES_INCOMPLETE.


PF — Passo 9: Aguardar aprovação

GET /v1/banking/onboarding/{id}
Authorization: Bearer <JWT>

Pollar (ou usar webhooks) até status virar:

  • COMPLETED → aprovado! A resposta inclui account com bank, agency, number da nova conta.
  • DECLINED → reprovado.
  • WAITING_CORRECTION → algum arquivo precisa ser refeito. Use o bloco analysis (abaixo) para saber quais. Volte ao passo 7.
  • WAITING_MANUAL_ANALYSIS → mesa manual da Dock. Aguarde (pode levar horas).
  • FAILED → erro permanente. O last_error.code indica a causa (DOCK_PROCESS_EXPIRED quando a Dock fechou por timeout — proposta precisa ser refeita).

Pronto. Conta aberta. Você pode emitir extrato, transferir, etc.


Quando arquivos são rejeitados — analysis.resend_files

Sempre que a Dock devolve um diagnóstico (em WAITING_CORRECTION, DECLINED ou FAILED por arquivos rejeitados), a resposta de GET /onboarding/{id} inclui dois blocos prontos pro frontend orientar o usuário.

Funciona pra PF e PJ

A estrutura é a mesma nos dois fluxos, mas em PJ cada arquivo rejeitado vem identificado com subject_type + subject_name — porque a rejeição pode estar na empresa OU em qualquer um dos sócios. Use isso pra mostrar "Reenvie a selfie do João" vs "Reenvie o contrato social da empresa".

last_error — mensagem amigável

PF:

"last_error": {
  "code": "DOCK_FILES_DECLINED",
  "message": "Arquivos rejeitados pela análise. Envie novamente: RG (verso) de Fernando, selfie de Fernando, RG (frente) de Fernando.",
  "occurred_at": "2026-05-05T11:05:47Z"
}

PJ:

"last_error": {
  "code": "DOCK_FILES_DECLINED",
  "message": "Arquivos rejeitados pela análise. Envie novamente: contrato social da empresa, selfie de João, RG (frente) de Maria."
}

Use last_error.message direto como banner — já está em PT-BR, cita os arquivos pelos nomes amigáveis e identifica o dono.

analysis — diagnóstico estruturado (exemplo PF)

"analysis": {
  "files_status": "DECLINED",
  "files_reason_code": "17",
  "data_result": "APPROVED",
  "data_info": "Data analysis approved.",
  "resend_files": [
    {
      "file_side": "IDENTITY_CARD_VERSE",
      "label": "RG (verso) de Fernando",
      "proposal_person_id": "019df4c9-ed05-6a4e-aa59-695a917f34c4",
      "subject_type": "PARTNER",
      "subject_name": "fernando roberto de paula"
    },
    { "file_side": "SELFIE", "label": "selfie de Fernando", "proposal_person_id": "019df4c9-ed05-...", "subject_type": "PARTNER", "subject_name": "fernando roberto de paula" }
  ],
  "invalid_files": [
    {
      "file_type": "IDENTIFICATION_FILE",
      "file_category": "IDENTITY_CARD",
      "file_side": "IDENTITY_CARD_VERSE",
      "status": "INVALID",
      "proposal_person_id": "019df4c9-ed05-...",
      "subject_type": "PARTNER",
      "subject_name": "fernando roberto de paula",
      "subject_document": "26924591809"
    }
  ]
}

analysis — diagnóstico estruturado (exemplo PJ)

"analysis": {
  "files_status": "DECLINED",
  "resend_files": [
    {
      "file_side": "ARTICLES_OF_ASSOCIATION",
      "label": "contrato social da empresa",
      "proposal_person_id": "019d-empresa-uuid",
      "subject_type": "COMPANY",
      "subject_name": "ACME Tecnologia LTDA"
    },
    {
      "file_side": "SELFIE",
      "label": "selfie de João",
      "proposal_person_id": "019d-joao-uuid",
      "subject_type": "PARTNER",
      "subject_name": "João da Silva"
    },
    {
      "file_side": "IDENTITY_CARD_FRONT",
      "label": "RG (frente) de Maria",
      "proposal_person_id": "019d-maria-uuid",
      "subject_type": "PARTNER",
      "subject_name": "Maria Costa"
    }
  ]
}
CampoComo usar
analysis.resend_files[]Lista pronta pro front — itere e mostre "Reenvie estes documentos: ..." usando label.
analysis.resend_files[].subject_type"COMPANY" ou "PARTNER". Use pra agrupar a UI: 1 cartão pra empresa, 1 cartão por sócio.
analysis.resend_files[].subject_nameNome do sócio (ou razão social) — pode usar direto na UI.
analysis.resend_files[].proposal_person_idUUID do proposal_person na Dock. Use pra rotear o reupload no endpoint certo (/files/pj/partners/{partnerId} ou /files/pj/company).
analysis.invalid_files[]Detalhes crus da Dock + os mesmos campos subject_* + subject_document (CPF/CNPJ).
analysis.files_statusAPPROVED / DECLINED / "" (sem análise ainda).
analysis.data_resultMesmo, mas pra dados pessoais (CPF, endereço).
analysis.files_reason_codeCódigo numérico cru retornado pela Dock (ex: "14", "21"). Veja o catálogo abaixo.

Catálogo oficial de files_reason_code (Dock)

Códigos retornados em analysis.files_reason_code quando os arquivos individuais ficam VALID mas a análise consolidada é DECLINED:

reasonCodeaffectedDocumentreasonDescriptionConceito
0DOCUMENTDocument not acceptedDocumento enviado não é um documento aceito pelo módulo de validação
1DOCUMENTUploaded image is not a document photoA imagem que foi enviada não é uma foto de documento
2DOCUMENTIllegible document photoO documento está ilegível.
3DOCUMENTCropped document photoO documento está cortado.
4DOCUMENTExpired documentDocumento fora da validade aceita.
5SELFIELow-quality selfieSelfie com baixa qualidade.
6SELFIEUploaded image is not a selfieA imagem enviada não é uma selfie.
7SELFIEFace is not visibleO rosto não está visível.
8ALLInconsistent selfie (facematch)A selfie e a foto do documento possuem baixa similaridade.
9ALLInconsistent document (automatic validation)Dados e/ou documentos com alto risco de ação maliciosa
10ALLScreenphotoA foto do documento ou da selfie não foi tirada diretamente da pessoa ou documento.
11ALLPhotocopyA foto de documento ou da selfie é uma fotocópia.
12ALLInconsistent registration (document does not match with registration)Cadastro inconsistente (o documento não corresponde ao cadastro)
13DOCUMENTDigital driver license without QR CodeCNH digital está sem o QR Code
14ALLFace in Fraudbook (high similarity)Face com avaliação biométrica inconsistente
15ALLDocument in Fraudbook (high similarity)Foto do documento com avaliação biométrica inconsistente
16NONEInconsistent dataConjunto de dados informados apresenta alto risco de ação maliciosa.
17ALLFace not foundNão foi localizada uma face na selfie ou documento.
19DOCUMENTThe loaded document type does not match the document categoryO tipo de documento carregado é diferente do informado.
20ALLrisk policyPessoa encontra-se com restrição junto a política de risco de AML dentro da Dock.
21ALLInconsistent biometricFace com avaliação biométrica inconsistente
24NONEInconsistent dataConjunto de dados informados apresenta alto risco de ação maliciosa.
25NONEVictim of ideological falsehoodConjunto de dados informados são de vítimas de fraude ideológica

Observação sobre o código 21 (catálogo Dock):

  • Resposta automática: o CPF foi encontrado na base de Cadastros Ativos da Oiti e a face não teve correspondência por similaridade biométrica com a face cadastrada para o mesmo CPF. Face e CPF foram certificados negativamente por baixa similaridade.
  • Resposta manual: mesa identificou um caso de fraude biométrica e entendeu que essa face deve ser inclusa no book de faces fraudadoras.

A mensagem em last_error.message é montada usando exatamente o texto da coluna "Conceito" acima, sem texto adicional.

Boas práticas pra selfies e documentos (oficial Dock)

Mostre estas dicas antes do usuário tirar as fotos pra reduzir reprovações:

Selfie

  • Foto recente, refletindo a aparência atual (não vale foto antiga ou digitalizada).
  • Fundo branco ou esbranquiçado, ambiente bem iluminado, rosto inteiro de frente para a câmera, ambos os olhos abertos.
  • Apenas o usuário na imagem (sem outras pessoas).
  • Não segurar documento na selfie — vai em foto separada.
  • Sem máscara, óculos escuros, chapéu ou acessório que cubra o rosto.
  • Sem sombras ou reflexos de luz.
  • Formato .jpg ou .png, mínimo 600×600 px, orientação vertical.

Documentos (RG/CNH/CIN físicos)

  • Documento dentro da validade (até 10 anos da emissão).
  • Frente e verso em fotos separadas.
  • Tirar capas plásticas mesmo transparentes — reflexo deixa a foto ilegível.
  • Sem reflexos; refazer se sair embaçada.
  • Não usar fotos digitalizadas ou capturas de tela — caem em revisão manual ou são recusadas.
  • CNH física dobrada: enviar as duas partes em fotos separadas, não a CNH aberta inteira.
  • Não enviar a mesma imagem duplicada em categorias diferentes.
  • Formato .jpg ou .png, mínimo 600×600 px.

CNH Digital (categoria DIGITAL_DRIVER_LICENSE, único arquivo — sem frente/verso)

  • Formato .pdf ou .jpg, exportada direto do app "Carteira Digital de Trânsito" (mostra documento aberto + QR Code à direita).
  • Não aceito: capturas de tela, frente/verso/QR separados.

Carteira de Identidade Nacional (CIN) — usar categorias IDENTITY_CARD_FRONT e IDENTITY_CARD_VERSE normalmente, formatos .pdf ou .jpg.

Erros de validação KYC (data_result: DECLINED)

Quando os dados pessoais (CPF/nome/mãe/nascimento) caem na validação KYC, a Dock devolve um destes códigos. Aparecem em analysis.data_info ou no body do erro 409 ao tentar criar o processo:

CódigoDescrição
1000Registro nacional não pôde ser validado — tentar novamente em 1 min; se persistir, dados não podem ser validados
1001Status do documento não está regular
1002Nome não corresponde a fontes oficiais do governo
1003Nome da mãe não corresponde a fontes oficiais
1004Data de nascimento não corresponde a fontes oficiais
1005Operação não permitida para o documento (sanções)
1006Recusado pela política de risco interna
1007Rejeitado por política de risco DICT / Resolução BCB 6/2023

Em 1002, 1003 e 1004, oriente o usuário a revisar exatamente o campo correspondente. Em 1000, sugerir nova tentativa em 1 minuto. Demais códigos: encaminhar ao suporte da Autra com o proposal.id.

Como reagir no front (PF)

  1. Se status = WAITING_CORRECTION: itere analysis.resend_files e refaça só esses via POST /onboarding/{id}/files (passo 7).
  2. Se status = DECLINED ou FAILED: a proposta é terminal. Mostre last_error.message e crie nova proposta.
  3. Se analysis.data_result = "DECLINED": dados pessoais foram rejeitados (raro). Revise CPF, nome da mãe, data de nascimento — analysis.data_info traz a explicação.

Como reagir no front (PJ)

  1. Agrupe resend_files por subject_type + subject_name — uma seção "Documentos da empresa", uma seção por sócio.
  2. Roteie o reupload baseado em subject_type:
    • COMPANYPOST /onboarding/{id}/files/pj/company com file_side (passo 10).
    • PARTNER → use o proposal_person_id retornado pra encontrar o partnerId Autra correspondente na sua proposta, e chame POST /onboarding/{id}/files/pj/partners/{partnerId} (passo 11). O dock_proposal_person_id do sócio voltou no partners[] da resposta de /start-pj.
  3. Sócios convidados (que recebem link) só veem os arquivos deles — filtre resend_files no frontend público pelo subject_name ou subject_document que casa com o sócio do convite.

🟩 Fluxo PJ — passo a passo

PJ tem 2 fases antes da Dock:

  1. Master configura tudo — empresa + sócios (com convites se preciso).
  2. Sócios convidados preenchem seus dados (rota pública, sem login).
  3. Só depois o master dispara start-pj.
App/Site (master)     BFF Autra              Dock           Sócio convidado
-----------------     ----------             -----          ----------------
master  ─────────►    /onboarding {PJ}
                      /legal-entity
                      /partners (stub)
                      /partners/{id}/invite ──────────────►  recebe link
                                                             ▼
                                                             /invite/{token}
                                                             /invite/{token}/personal-data
                      /start-pj ──────────►  POST /starting
                      /files/pj/company ──►  POST /files
                      /files/pj/partners/{id} ─► POST /files
                      ◄── webhooks ────────  process_finished

PJ — Passo 1: Criar a proposta

POST /v1/banking/onboarding
Authorization: Bearer <JWT do master>
Idempotency-Key: pj-acme-2026-05-01

{ "type": "PJ" }

Resposta 201: {id, type: PJ, status: DRAFT}. Guarde o id.


PJ — Passo 2 e 3: Termos (igual ao PF)

GET  /v1/banking/onboarding/{id}/terms
POST /v1/banking/onboarding/{id}/terms/accept
{
  "tou_token": "...",
  "pp_token":  "...",
  "fingerprint": "Mozilla/5.0...#187.45.32.10"
}

status: TERMS_ACCEPTED.


PJ — Passo 4: Salvar dados da empresa

PUT /v1/banking/onboarding/{id}/legal-entity
Authorization: Bearer <JWT>

{
  "cnpj":                       "60116920000100",
  "legal_name":                 "ACME Tecnologia LTDA",
  "trade_name":                 "ACME",
  "legal_nature":               "2062",
  "establish_date":             "2020-01-15",
  "industrial_classification":  "6201500",
  "business_classification":    "Desenvolvimento de software sob encomenda",
  "revenue":                    5000000,
  "address": { "zip_code": "06404250", "street": "Anápolis", ... },
  "phone":   { "area_code": "11", "number": "914052149" },
  "email":   "[email protected]"
}

Resposta 200: status: LEGAL_ENTITY_FILLED.

Códigos de natureza jurídica suportados:

CódigoTipo
2135MEI / Empresário Individual
2305 / 2313EIRELI
2062LTDA
2046 / 2054S.A.
2143 / 2330Cooperativa

⚠️ Validamos: CNPJ (check digits + único no tenant) + CNAE numérico + data de constituição não-futura.


PJ — Passo 5: Adicionar sócios

Você precisa cadastrar todos os sócios que aparecem no contrato social. Há 2 modos:

Modo A — Sócio com dados completos (master ou sócio que está ao seu lado)

POST /v1/banking/onboarding/{id}/partners
Authorization: Bearer <JWT>

{
  "role":                       "PARTNER",
  "person_type":                1,
  "is_account_handler":         true,
  "is_responsible_onboarding":  true,
  "personal_data": {
    "full_name":   "Jorge Augusto Silva",
    "preferred_name": "Jorge",
    "cpf":         "39053344705",
    "birth_date":  "1985-03-10",
    "mother_name": "Maria da Silva",
    "gender_id":         3,
    "marital_status_id": 8,
    "is_pep":            false,
    "email":   "[email protected]",
    "phone":   { "area_code": "11", "number": "987654321" }
  },
  "address":  { "zip_code": "06404250", ... }
}

→ Status do sócio: READY.

Modo B — Sócio "stub" (vai receber convite)

POST /v1/banking/onboarding/{id}/partners
Authorization: Bearer <JWT>

{
  "role":                       "PARTNER",
  "person_type":                1,
  "is_responsible_onboarding":  false,
  "email_invite":               "[email protected]",
  "phone_invite":               { "area_code": "11", "number": "999887766" }
}

→ Status do sócio: INVITED. Proposta vai pra PARTNERS_PENDING.

⚠️ Regras:

  • Exatamente 1 sócio precisa ter is_responsible_onboarding=true (o "master").
  • CPFs precisam ser únicos dentro da proposta (409 DUPLICATE_PARTNER_CPF se duplicar).
  • gender_id e marital_status_id vêm de GET /onboarding/setup-types — cache esse catálogo no front.

PJ — Passo 6: Gerar convite para sócios stub

Para cada sócio stub (Modo B do passo 5):

POST /v1/banking/onboarding/{id}/partners/{partnerId}/invite
Authorization: Bearer <JWT>

{ "channels": ["EMAIL", "WHATSAPP"] }

Resposta 201:

{
  "token":       "eyJhbGciOiJIUzI1NiIs...",
  "expires_at":  "2026-05-08T16:00:00Z",
  "invite_url":  "https://autra.io/onboarding/invite/eyJhbGc...",
  "channels": {
    "email":    { "mailto": "mailto:[email protected]?subject=...&body=..." },
    "whatsapp": { "wa_me":  "https://wa.me/5511999887766?text=..." }
  }
}

O backend NÃO envia o email/WhatsApp. Ele te dá os deep links prontos. O master clica no mailto: (abre o app de email) ou no wa.me/ (abre WhatsApp já com a mensagem) e dispara do próprio dispositivo.

⚠️ Se você gerar um convite novo para o mesmo sócio, o anterior é revogado (vira INVITE_TOKEN_REVOKED).


PJ — Passo 7: Sócio convidado preenche dados (rota PÚBLICA)

Esta etapa não é o master que faz. É o sócio que recebeu o link.

O frontend público (sem login Cognito) carrega a página https://autra.io/onboarding/invite/{token} e chama:

7a. Buscar o estado atual do convite

GET /v1/banking/onboarding/invite/{token}

(sem Authorization)

Resposta 200:

{
  "company_name":  "ACME Tecnologia LTDA",
  "partner_role":  "PARTNER",
  "needs":         ["personal_data", "address"]
}

7b. Submeter os dados pessoais

PUT /v1/banking/onboarding/invite/{token}/personal-data

{
  "personal_data": { "full_name": "...", "cpf": "...", ... },
  "address":       { ... }
}

→ Sócio fica READY.

⚠️ O sócio convidado não pode alterar role, person_type, is_account_handler ou is_responsible_onboarding — o backend bloqueia.

⚠️ Quando todos os sócios estiverem READY + houver 1 master, a proposta vai pra PARTNERS_READY. Só então o master pode chamar start-pj.


PJ — Passo 8: Iniciar processo na Dock

(De volta ao master.)

POST /v1/banking/onboarding/{id}/start-pj
Authorization: Bearer <JWT do master>

{
  "device_id":        "uuid",
  "device_model":     "iPhone 15",
  "operating_system": "iOS 18",
  "ip_address":       "187.45.32.10"
}

⚠️ Pré-condição: status = PARTNERS_READY. Se não estiver, retorna 409 INVALID_STATUS.

Resposta 200:

{
  "status": "PROCESS_STARTED",
  "process_instance_id": "...",
  "dock_proposal_id":    "...",
  "partners": [
    { "id": "p1", "dock_proposal_person_id": "..." },
    { "id": "p2", "dock_proposal_person_id": "..." }
  ]
}

✅ Cada sócio agora tem um dock_proposal_person_id. A empresa também tem um na raiz (dock_proposal_person_id).


PJ — Passo 9: Esperar liberação de upload (polling)

Igual ao PF passo 6:

GET /v1/banking/onboarding/{id}/files/pending
  • 202 → aguardar e repolar.
  • 200 → resposta lista o que cada proposal_person_id precisa enviar.

PJ — Passo 10: Upload dos documentos da empresa

Documentos exigidos variam pela natureza jurídica:

NaturezaDocumentos
MEIMEI_CCMEI
LTDAARTICLES_OF_ASSOCIATION (contrato social) + última alteração
S.A.COMPANY_BYLAWS (estatuto) + ata da última eleição
EIRELIARTICLES_OF_ASSOCIATION

Para cada documento:

POST /v1/banking/onboarding/{id}/files/pj/company
Authorization: Bearer <JWT do master>

{
  "file_side": "ARTICLES_OF_ASSOCIATION",
  "base64":    "<conteúdo do PDF em base64>"
}

✅ Roteia automaticamente para o proposal_person_id da empresa.

PJ de sócio único (MEI / dono único): a Dock abre o slot do documento da empresa de forma assíncrona — a proposta pode já estar em PROCESSING quando o contrato social/CCMEI for enviado. Por isso este endpoint aceita o doc da empresa também com a proposta em PROCESSING/WAITING_MANUAL_ANALYSIS (não só em FILES_PENDING). Se o upload voltar 409 INVALID_STATUS logo após o start-pj, aguarde alguns segundos e tente de novo — o slot ainda está sendo criado.


PJ — Passo 11: Upload da selfie e do documento de identificação de cada sócio

Para cada sócio (master + Regulares), via SDK Unico:

POST /v1/banking/onboarding/{id}/files/pj/partners/{partnerId}
Authorization: Bearer <JWT>

{ "file_side": "SELFIE", "jwt": "<JWT do Unico SDK>" }

Repita com os file_sides do documento escolhido (RG/CIN: IDENTITY_CARD_FRONT + IDENTITY_CARD_VERSE; CNH física: DRIVER_LICENSE_FRONT + DRIVER_LICENSE_VERSE; CNH digital: DIGITAL_DRIVER_LICENSE; RNE: RNE_FRONT + RNE_VERSE; Passaporte: PASSPORT). A lista de opções vem do groups[] em GET .../files/pending.

⚠️ O sócio Regular faz isso acessando o link de convite — depois de preencher os dados (passo 7), o frontend público continua para a etapa de selfie/documento no mesmo dispositivo dele (com o JWT de convite, não o do master).


PJ — Passo 12: Marcar uploads completos

POST /v1/banking/onboarding/{id}/files/complete
Authorization: Bearer <JWT do master>

status: FILES_UPLOADED.


PJ — Passo 13: Aguardar aprovação

Igual ao PF:

GET /v1/banking/onboarding/{id}

Estados terminais: COMPLETED (com account populada), DECLINED, WAITING_CORRECTION (refazer arquivo), WAITING_MANUAL_ANALYSIS (PJ com 1 sócio só).

Pronto. Conta PJ aberta.


Sobre BACEN 491 — dispositivo autorizado para Pix

⚠️

Conta aberta (COMPLETED) NÃO significa device autorizado pra Pix. O device_id que você mandou em POST /start / POST /start-pj serve só pro KYC do onboarding — ele não é registrado na Trusted Devices API da Dock. Antes de qualquer operação Pix/DICT você precisa registrar o dispositivo (passo abaixo). Pular isso faz o Pix/DICT retornar NO_AUTHORIZED_DEVICE (e, na Dock, PIX-4002).

Passo obrigatório após o COMPLETED — registrar o device

O BACEN 491/2024 exige um dispositivo autorizado (registrado + confirmado por MFA) pra operações Pix mutantes:

  • Criar/remover chaves Pix DICT
  • Enviar Pix (P2P, TED out)
  • Gerar QR Code dinâmico

Fluxo de autorização (SMS MFA) — é o único caminho, não um extra:

  1. POST /v1/banking/devices — inicia o registro e dispara o SMS
  2. POST /v1/banking/devices/{id}/mfa-confirm — confirma o código recebido → device fica autorizado
  3. (se precisar) POST /v1/banking/devices/{id}/mfa-resend — reenvia o código

Depois disso, ao chamar POST /v1/banking/accounts/{id}/pix-keys (ou qualquer rota Pix), o backend resolve o dispositivo autorizado automaticamente. Enquanto não houver device autorizado, essas rotas respondem NO_AUTHORIZED_DEVICE (422) — esse é o sinal pro app disparar o fluxo acima.

Gerenciar dispositivos

  • GET /v1/banking/devices — lista devices autorizados da conta
  • DELETE /v1/banking/devices/{id} — revoga device perdido/roubado
  • Para um segundo aparelho (troca de celular, tablet), repita o mesmo fluxo POST /v1/banking/devices + mfa-confirm.

Detalhes completos no Guia Trusted Devices.

Limites pra dispositivos NÃO autorizados (BACEN 491)

  • Pix: R$ 200 por transação e R$ 1.000 por dia
  • DICT (chaves Pix): bloqueado — qualquer create/delete/portability exige device autorizado
  • Devices inativos por mais de 12 meses são bloqueados automaticamente pela Dock

Webhooks (opcional — recomendado em produção)

Se você assina os webhooks da Autra, evita polling. Endpoint que você expõe:

POST <seu-endpoint> com body cifrado em AES-256-GCM (chave compartilhada via Setup).

Eventos enviados durante o onboarding:

event_typeQuando ocorreO que fazer
process_startedLogo após /start ou /start-pjApenas registrar
process_kyc_analysis_startedDock começou KYCApenas registrar
process_files_receives_startedUpload liberadoFrontend pode chamar /files/pending agora
process_files_corrections_startedAlgum arquivo foi rejeitadoNotificar usuário, pedir refile
process_data_corrections_startedAlgum dado foi rejeitadoNotificar usuário
process_files_analysis_startedAnálise de arquivos rolandoApenas registrar
process_desk_analysis_startedMesa manual (só PJ 1 sócio)Avisar usuário que pode levar horas
process_registries_startedCadastro nos órgãosQuase lá
process_finishedProposta finalizadaBuscar status final via GET /onboarding/{id}
process_failedErro na DockTratar como FAILED

Idempotência (importante)

  • POST /onboarding aceita Idempotency-Key — mesma key = mesma proposta.
  • POST /partners (PJ) valida CPF único na proposta.
  • POST /partners/{id}/invite (PJ) revoga token anterior — sempre.
  • POST /start e POST /start-pj são idempotentes — reenviar não recria na Dock.

Erros comuns

CodeHTTPO que fazer
INVALID_STATUS409Você chamou no estado errado. Cheque GET /onboarding/{id} antes.
MISSING_DEVICE_INFO400Faltou device_id no payload de /start.
PJ_ONLY409Você chamou rota PJ em proposta PF (ou vice-versa).
INVALID_LEGAL_ENTITY422CNPJ/CNAE/data inválidos. Releia o passo 4 PJ.
INVALID_PARTNER422Dados do sócio incompletos.
MASTER_PARTNER_REQUIRED422Você tentou start-pj sem 1 sócio com is_responsible_onboarding=true.
DUPLICATE_PARTNER_CPF409CPF já está em outro sócio desta proposta.
CANNOT_REMOVE_MASTER409Promova outro a master antes de remover este.
PARTNER_NOT_FOUND404partnerId não existe nesta proposta.
INVITE_TOKEN_EXPIRED401Convite passou de 7d. Gere outro.
INVITE_TOKEN_REVOKED401Você gerou um convite novo — use o atual.
DOCK_FILES_DECLINED— (em last_error.code)Análise de arquivos rejeitada. Use analysis.resend_files para refazer só os marcados como INVALID.
DOCK_DATA_DECLINED— (em last_error.code)Análise de dados rejeitada. Cheque analysis.data_info — geralmente CPF, nome da mãe ou data de nascimento.
DOCK_AWAITING_CORRECTION— (em last_error.code)Dock pediu correção mas a Autra ainda não tem detalhe. Faça novo GET /onboarding/{id} e siga o analysis.
DOCK_PROCESS_EXPIRED— (em last_error.code)Proposta expirou na Dock (timeout). Status vira FAILED. Crie uma nova proposta.

⚠️ 202 em /files/pending NÃO é erro. É "aguarde e tente de novo em ~2s".


Boas práticas

  • Cache setup-types no frontend — gender_id, marital_status_id etc. raramente mudam.
  • Polling com backoff: 2s → 4s → 8s → 16s (máx 30s).
  • Use webhooks em produção — polling fica para desenvolvimento/teste.
  • CPFs mascarados na resposta (123.***.***-45). Não tente reconstruir; peça de novo se precisar exibir.
  • Idempotency-Key sempre que possível em POST /onboarding (evita duplicar proposta se a chamada falhar e for retentada).
  • (PJ) Convites: deixe o master escolher os canais (EMAIL/WHATSAPP) — alguns sócios preferem um, outros outro.

Limitações conhecidas

  • Sócios person_type=2 (PJ-relacionada) — suportado mas raro.
  • Procuradores (role: ATTORNEY) não podem ser master.
  • Mesa manual (process_desk_analysis_started) só dispara para PJ com 1 único sócio.
  • Recovery automático de processo Dock pré-existente: hoje só PF; em PJ você precisa cancelar e refazer.
  • Após PROCESS_STARTED, a composição societária está congelada na Dock — não dá pra adicionar/remover sócios. Cancele e refaça.

FAQ

Posso trocar o type (PF/PJ) depois de criar a proposta? Não. Cancele e crie outra.

O master precisa ter conta na Autra antes de criar a proposta? Sim — autenticação Cognito obrigatória.

Sócio convidado (PJ) precisa criar login Cognito? Não — ele acessa pelo deep link público.

Posso reenviar convite se o sócio não respondeu? Sim. POST /partners/{id}/invite gera token novo e revoga o anterior.

O backend manda mesmo o email/WhatsApp pro sócio? Não. Ele te dá mailto: e wa.me/?text=... prontos — o master abre do próprio dispositivo.

Posso adicionar sócios depois de start-pj? Não. Composição congelada na Dock.

Como sei que a conta foi aprovada? Polla GET /onboarding/{id} ou aguarde webhook process_finished. Quando aprovada, a resposta tem account com bank, agency, number.

Quanto tempo leva o onboarding? PF: minutos a algumas horas. PJ: pode levar 1 dia útil (mais documentos).

Onde vejo o que falta upar? GET /onboarding/{id}/files/pending — lista o file_side exigido por proposal_person_id.