Referência da API ONP
Este documento descreve como integrar com a API de onboarding da Guardline. Ele cobre autenticação, criação de sessões de onboarding, declaração de sujeitos vinculados (o representante legal para kyc_minor na criação da sessão), resolução dos links de sessão, acompanhamento do ciclo de vida da execução, recebimento de notificações via webhook e polling de eventos perdidos.
O modelo de integração segue uma separação clara de responsabilidades: a Guardline verifica e decide, o integrador orquestra. O integrador declara o onboarding com uma única chamada de API, distribui as URLs retornadas na resposta (ou pede que a Guardline as entregue) e recebe notificações de progresso e de decisão final via webhook.
Visão geral
Seção intitulada “Visão geral”O fluxo de integração envolve quatro participantes:
| Participante | Responsabilidade |
|---|---|
| Backend do integrador | Cria sessões, recebe webhooks, consulta execuções |
| API da Guardline | Gerencia sessões, tokens, execuções, sujeitos vinculados e notificações |
| Frontend do integrador | Renderiza o link diretamente ou monta um iframe apontando para ele |
| Onboarding Guardline | Conduz a jornada de verificação com o cliente final |
Uma única chamada POST /api/v1/onboarding/sessions cria um onboarding para qualquer tipo de fluxo. A requisição carrega os dados do sujeito principal mais um array opcional de sujeitos vinculados. A resposta carrega uma URL por sujeito criado. A notificação dos sujeitos vinculados é opt-in por chamada e sai pelos canais configurados para o fluxo (SMS e e-mail por padrão, WhatsApp quando solicitado).
Para kyc_minor, o integrador declara na mesma requisição tanto o menor de idade (em primary) quanto o representante legal (em linked_subjects[]). A jornada de cada um é independente: o menor abre a sua URL, o representante abre a dele, e a Guardline orquestra a consolidação. O integrador decide se a Guardline entrega a URL do representante pelos canais de notificação do fluxo (notify.linked: true) ou se a entrega fica a cargo do integrador (notify.linked: false ou omitido).
Todas as chamadas de API seguem o padrão REST sobre HTTPS, com payloads JSON e respostas no formato de envelope padrão da Guardline.
Ambientes e autenticação
Seção intitulada “Ambientes e autenticação”Ambientes
Seção intitulada “Ambientes”| Ambiente | URL base | Prefixo da API key |
|---|---|---|
| Sandbox | https://{instance}.onp.dev.guardline.com.br | gl_test_ |
| Produção | https://{instance}.onp.prod.guardline.com.br | gl_live_ |
O valor {instance} é provisionado durante a configuração do integrador e identifica a infraestrutura dedicada. Cada instância tem banco de dados, aplicação e armazenamento próprios e isolados.
Autenticação
Seção intitulada “Autenticação”Todas as chamadas de API voltadas ao integrador exigem a chave correspondente ao ambiente. Dois formatos de header são aceitos de forma equivalente, X-API-Key e Authorization: Bearer. Envie um dos dois:
X-API-Key: gl_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Authorization: Bearer gl_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2As chaves são provisionadas pelo painel administrativo da Guardline. O token em texto claro é exibido apenas no momento da criação. Internamente, a Guardline armazena o hash SHA-256 da chave.
O prefixo é um rótulo escolhido na criação da chave, não um mecanismo de roteamento, e assume live por padrão quando o ambiente não é especificado. Uma chave emitida em uma instância de sandbox sem ambiente explícito aparece, portanto, como gl_live_ mesmo sendo uma chave exclusiva de sandbox. O prefixo não diz nada sobre qual instância vai aceitá-la: uma chave autentica apenas na instância que a emitiu.
Isso vale para X-API-Key, cujo valor é sempre lido como uma API key. Em Authorization: Bearer, o prefixo de fato roteia: a credencial só é tratada como API key quando começa com gl_live_ ou gl_test_, e qualquer outro valor nesse header é verificado como um JWT. Uma chave sem prefixo enviada como Bearer, portanto, falha na verificação do JWT e responde 401 INVALID_TOKEN. Envie uma chave assim em X-API-Key.
Um problema de credencial sempre responde 401, nunca 400. MISSING_TOKEN quando nenhum header foi enviado, INVALID_API_KEY quando a chave é desconhecida, revogada ou expirada. Um 400 significa que a chave foi aceita e o corpo da requisição foi rejeitado, portanto reemitir a chave não resolve.
Requisições HTTP são rejeitadas. HTTPS com TLS 1.2 ou superior é obrigatório em todos os ambientes.
Duas superfícies fogem dessa regra:
| Endpoint | Autenticação |
|---|---|
POST /api/v1/onboarding/executions/{execution_id}/resend-representative-link | JWT de admin ou supervisor do backoffice da Guardline. Consulte Reenvio do link do representante. |
GET /api/v1/link/{token} | Público. A posse do token é a credencial de acesso, e a rota tem limite de requisições por IP de origem. |
Idempotência
Seção intitulada “Idempotência”Endpoints que alteram estado aceitam o header de requisição opcional Idempotency-Key (qualquer string única de no máximo 255 caracteres, normalmente um UUID; uma maior retorna 400 INVALID_IDEMPOTENCY_KEY). Quando presente, o servidor guarda a resposta em cache por 15 minutos; chamadas seguintes com a mesma chave retornam a resposta em cache com o header de resposta Idempotency-Replayed: true. Sem o header, cada chamada é processada de forma independente.
O cache é indexado pelo par (chave, endpoint), portanto reutilizar uma mesma chave em dois endpoints diferentes não gera colisão.
Use o header ao repetir uma chamada após um timeout de rede, para evitar execuções duplicadas. Gere uma chave nova para cada nova operação lógica.
Formato da resposta
Seção intitulada “Formato da resposta”Todas as respostas seguem o envelope padrão:
Sucesso:
{ "error": false, "data": {}}Erro:
{ "error": true, "message": "descrição do erro", "code": "ERROR_CODE", "request_id": "550e8400-e29b-41d4-a716-446655440000"}O campo message é localizado. Os integradores devem ramificar a lógica pelo campo code (estável e não traduzido), não pelo texto da mensagem.
O campo request_id é retornado apenas nas respostas de erro e pode ser usado para rastreamento em chamados de suporte. Uma resposta de sucesso carrega error e data, mais um array opcional warnings com entradas {code, message, context} que reporta degradações não fatais sem transformar a resposta em um erro. warnings é omitido quando não há nada a reportar.
Idioma das mensagens de erro
Seção intitulada “Idioma das mensagens de erro”O campo message é resolvido a partir do header de requisição Accept-Language. Quem não envia nenhum header recebe o idioma padrão da instância (pt-BR, salvo se a instância estiver configurada de outra forma).
Accept-Language | Resultado |
|---|---|
| ausente, não interpretável ou sem correspondência | o idioma padrão da instância (pt-BR, salvo se a instância estiver configurada de outra forma) |
pt-BR | pt-BR |
es-AR, es sem região, e as variantes latino-americanas como es-UY | Espanhol (Argentina) |
curl https://{instance}.onp.dev.guardline.com.br/api/v1/link/invalido \ -H "Accept-Language: es-AR"{ "error": true, "message": "Enlace no encontrado", "code": "LINK_NOT_FOUND", "request_id": "77440a78-6e11-4def-8c08-31b1189ccbd4"}O catálogo é indexado pelo code do erro. Um código sem entrada no idioma solicitado cai para a sua mensagem em pt-BR, e não para um texto em branco ou um placeholder, de modo que a resposta nunca fica vazia e o code é sempre a referência autoritativa.
Por isso, os integradores nunca devem comparar com o texto da mensagem, nem montar a partir dele um texto exibido ao cliente final: ramifique a lógica pelo code e renderize o seu próprio texto.
Jornadas disponíveis
Seção intitulada “Jornadas disponíveis”GET /api/v1/onboarding/flowsRetorna a lista de jornadas configuradas e ativas do integrador, com os metadados necessários para criar sessões corretamente.
kyc_representative e kyb_representative são filtrados no servidor e não aparecem: esses filhos são criados apenas como entradas de linked_subjects[] (representative em uma sessão kyc_minor, officer em uma sessão kyb). O fluxo de KYC dos beneficiários finais de uma empresa kyb também não aparece, porque só a própria plataforma o abre. Apontar o flow_id para qualquer um deles é rejeitado com 400 INVALID_FLOW_TYPE. Selecione as jornadas pelo campo type, e não pela posição, e trate kyc, kyb e kyc_minor como o conjunto que pode ser iniciado.
Resposta
Seção intitulada “Resposta”{ "error": false, "data": [ { "flow_id": "61000000-0000-0000-0000-000000000001", "flow_type": "kyc", "name": "KYC Pessoa Física", "description": "Verificação de identidade para maiores de 18 anos.", "status": "active", "steps_count": 8, "steps": [ { "id": "cpf", "name": "CPF", "order": 1 }, { "id": "personal_data", "name": "Dados pessoais", "order": 2 }, { "id": "contact", "name": "Informações de contato", "order": 3 }, { "id": "professional_data", "name": "Dados profissionais", "order": 4 }, { "id": "address", "name": "Endereço", "order": 5 }, { "id": "terms", "name": "Termos de uso", "order": 6 }, { "id": "document", "name": "Documentoscopia", "order": 7 }, { "id": "biometric", "name": "Biometria facial", "order": 8 } ], "primary_schema": [ { "field": "full_name", "type": "string", "required": false, "description": "Nome completo" }, { "field": "tax_id", "type": "string", "required": false, "description": "CPF (somente dígitos ou com máscara)" }, { "field": "email", "type": "string", "required": false, "description": "Endereço de e-mail" }, { "field": "phone", "type": "string", "required": false, "description": "Telefone com código do país" }, { "field": "birth_date", "type": "string", "required": false, "description": "Data de nascimento (AAAA-MM-DD)" } ] }, { "flow_id": "61000000-0000-0000-0000-000000000010", "flow_type": "kyc_minor", "name": "KYC Menor de Idade", "description": "Verificação de identidade para menores entre 14 e 17 anos, com etapa adicional do representante legal.", "status": "active", "steps_count": 9, "steps": [ { "id": "cpf_birth_date", "name": "CPF e data de nascimento", "order": 1 }, { "id": "welcome", "name": "Boas-vindas", "order": 2 }, { "id": "personal_data", "name": "Dados pessoais do menor", "order": 3 }, { "id": "contact", "name": "Informações de contato", "order": 4 }, { "id": "professional_data", "name": "Dados profissionais e PEP", "order": 5 }, { "id": "address", "name": "Endereço", "order": 6 }, { "id": "terms", "name": "Termos de uso", "order": 7 }, { "id": "document", "name": "Documentoscopia do menor", "order": 8 }, { "id": "biometric", "name": "Biometria facial do menor", "order": 9 } ], "primary_schema": [ { "field": "full_name", "type": "string", "required": true, "description": "Nome completo do menor" }, { "field": "tax_id", "type": "string", "required": true, "description": "CPF do menor" }, { "field": "birth_date", "type": "string", "required": true, "description": "Data de nascimento do menor (AAAA-MM-DD), idade entre 14 e 17 anos" }, { "field": "phone", "type": "string", "required": false, "description": "Telefone do menor com código do país" }, { "field": "email", "type": "string", "required": false, "description": "Email do menor" } ] } ]}Cada entrada de fluxo também retorna flow_id (a versão específica do fluxo, utilizável como flow_id em POST /sessions) e steps_count. O steps_count é lido da coluna do próprio registro do fluxo, e não contado a partir do array steps da mesma resposta, portanto os dois podem divergir. Quando o número precisa estar correto, use o tamanho de steps.
O array primary_schema descreve os campos aceitos no bloco primary de POST /api/v1/onboarding/sessions. Para kyc_minor, o integrador declara ainda o representante legal em linked_subjects[] (consulte Sujeitos vinculados). A jornada do representante ocorre de forma independente da jornada do menor; o steps acima lista apenas as etapas do menor.
Criação de uma sessão
Seção intitulada “Criação de uma sessão”POST /api/v1/onboarding/sessionsCria uma nova sessão de onboarding e retorna uma URL por sujeito. Para kyc_minor, a resposta inclui uma URL separada para o representante legal vinculado, além da URL do menor de idade.
Esquema da requisição
Seção intitulada “Esquema da requisição”| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
flow_type | string | Um entre flow_type e flow_id | Tipo de jornada. Valores possíveis: kyc, kyb, kyc_minor. Só resolve quando a instância tem um único fluxo ativo desse tipo; com mais de um, responde 400 FLOW_ID_REQUIRED. Prefira sempre flow_id. |
flow_id | UUID | Um entre flow_type e flow_id | Versão específica do fluxo. Use este campo para fixar uma configuração de fluxo específica quando existirem vários fluxos do mesmo tipo. A resolução para um fluxo cujo tipo é de uso interno (como kyc_representative) é rejeitada com INVALID_FLOW_TYPE. |
channel | enum | Não | Origem da sessão: web, sdk, api. Padrão: web. O alias legado channel_code também é aceito; quando ambos são enviados, channel prevalece. |
layout_mode | enum | Não | Layout da interface da jornada: stepper ou chat. Não há padrão do lado da API: quando o campo é omitido, a jornada é renderizada com o layout configurado no próprio fluxo. |
reference_id | string (≤ 255) | Não | Correlacionador de formato livre para o sistema do integrador. Retornado nos webhooks e consultável via API. Recomendado: um identificador único e estável, como o ID da conta ou o ID da solicitação de onboarding. |
parent_execution_id | UUID | Não | Vincula esta execução a uma execução pai. Reservado para casos de uso avançados, como subfluxos de KYB; não é necessário para jornadas kyc/kyc_minor/kyb padrão. |
metadata | object | Não | Repasse de formato livre. Armazenado junto com a sessão e não devolvido por nenhum endpoint de leitura, portanto é somente escrita do lado do integrador. Use reference_id para um correlacionador que precise ser lido de volta. |
surface_hint | enum | Não | Defina como embed quando o integrador for renderizar a jornada dentro de um iframe. Controla a sanitização de PII em GET /api/v1/link/{token}. Consulte Surface hint (modo incorporado). |
primary | object | Obrigatório para kyc_minor; opcional para kyc e kyb | Dados do sujeito principal. Os campos são descritos pelo primary_schema retornado em GET /flows. Pode ser omitido (cold-start) para kyc e kyb; nesse caso, o registro do cliente é materializado quando o usuário envia a etapa de documento na jornada. primary.tax_id é validado conforme o tipo do fluxo resolvido; consulte Identificação fiscal do sujeito principal. |
linked_subjects | array | Obrigatório para kyc_minor (exatamente uma entrada, role: "representative"); opcional nos demais tipos de fluxo (entradas role: "officer", usadas no kyb) | Sujeitos vinculados ao principal. Consulte Sujeitos vinculados. |
notify | object | Não | Flags de opt-in de notificação. Consulte Opt-in de notificação. |
biometric | boolean | Não | Liga ou desliga a etapa de prova de vida facial apenas nesta sessão, sobrepondo a configuração da jornada: false suprime a etapa (a pessoa avança direto para a seguinte), true a habilita em uma jornada que a traz desligada, e a ausência mantém a configuração da jornada. Exige que o fluxo resolvido tenha a etapa de biometria (400 VALIDATION_FAILED caso contrário, nos dois sentidos). Vale apenas para a execução principal; as jornadas dos sujeitos vinculados mantêm a configuração do fluxo delas. O webhook terminal reflete a escolha em data.liveness.status (consulte data.liveness). |
partner | object | Não | Identifica quem opera a jornada pelo portal do próprio integrador, como o atendente de uma loja. O único campo é partner.email (até 254 caracteres), obrigatório quando o bloco é enviado: espaços nas pontas são removidos, e um bloco vazio ou um endereço inválido retorna 400 VALIDATION_FAILED. Não cria cliente nem execução vinculada. O endereço é guardado com a sessão; em instâncias com os e-mails de status ao parceiro habilitados, os e-mails de status da jornada vão para ele em vez de para o cliente, e uma sessão criada sem partner não os envia a ninguém. É também o destino dos avisos de correção cadastral (consulte Correção cadastral). |
Identificação fiscal do sujeito principal
Seção intitulada “Identificação fiscal do sujeito principal”primary.tax_id é validado contra o tipo do fluxo resolvido, e não contra o flow_type bruto da requisição, portanto uma requisição que envia apenas flow_id passa pela mesma verificação.
| Tipo do fluxo | Aceito em primary.tax_id |
|---|---|
kyc, kyc_minor | Um CPF válido: 11 dígitos, com os dígitos verificadores conferidos. |
kyb | Um CNPJ válido de 14 caracteres, numérico ou alfanumérico (IN RFB 2.229/2024), com os dígitos verificadores conferidos. |
A entrada formatada é aceita. O servidor remove a máscara antes da verificação (no CNPJ, também converte as letras para maiúsculas) e grava a forma limpa, portanto 123.456.789-09 e 12345678909 são o mesmo documento e encontram o mesmo cliente. Uma divergência retorna 400 INVALID_TAX_ID.
Sujeitos vinculados
Seção intitulada “Sujeitos vinculados”O array linked_subjects declara qualquer sujeito vinculado ao principal pelo contrato do fluxo. Hoje, isso cobre o representante legal do kyc_minor (representative) e os representantes legais de uma empresa kyb (officer).
Cada entrada tem:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
role | enum | Sim | Discriminador de papel: representative ou officer. O kyc_minor exige exatamente uma entrada com representative. officer declara um representante legal de uma empresa kyb, sem limite de quantidade: cada entrada cria uma execução filha no subfluxo de representante configurado no fluxo pai, e uma entrada com tax_id e full_name cria ou reutiliza o cadastro da pessoa. Um fluxo pai sem subfluxo de representante recusa a entrada com 400 VALIDATION_FAILED, e qualquer outro valor de role também retorna 400 VALIDATION_FAILED. |
subject | object | Sim | Bloco de dados do sujeito. Pode ser vazio ({}) para criação shell. |
O bloco subject aceita os seguintes campos:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
full_name | string | Pareado com tax_id (veja Obrigatoriedade dos campos abaixo) | Nome completo. |
tax_id | string | Pareado com full_name | Documento de identificação da pessoa. Este campo nunca é validado: qualquer string é aceita e a sessão é criada mesmo assim, com 201. Cabe a quem chama enviá-lo normalizado, apenas com dígitos; um valor formatado cria um cliente duplicado indexado pelo texto formatado. |
phone | string (E.164) | Não | Telefone com código do país. Não é exigido por notify.linked: o disparo precisa de phone ou email, e só é interrompido quando os dois estão ausentes. Validado contra a política de telefone do fluxo: um fluxo mobile-only rejeita telefones fixos com 422 PHONE_POLICY_VIOLATION. |
birth_date | string (YYYY-MM-DD) | Não | Data de nascimento. Quando informada, deve indicar 18 anos ou mais; um representante legal mais novo é rejeitado com 422 REPRESENTATIVE_AGE_BELOW_MINIMUM. |
email | string | Não | E-mail. |
relationship | enum | Obrigatório para kyc_minor; opcional caso contrário | Relação do representante legal com o menor. Obrigatório na criação da sessão para kyc_minor, independentemente de o restante de subject ser um shell. Valores: mother, father, grandparent, legal_guardian, tutor, other. Consulte Cardinalidade de kyc_minor. |
notification_channel | enum | Não | Canal preferencial para a entrega gerenciada pelo backend: sms, whatsapp ou email_sms. |
Obrigatoriedade dos campos de linked_subjects[i].subject
Seção intitulada “Obrigatoriedade dos campos de linked_subjects[i].subject”O serviço aceita três formatos:
| Formato | Conteúdo | Efeito |
|---|---|---|
| Shell | subject sem tax_id e full_name ao mesmo tempo (em kyc_minor, ele ainda carrega relationship) | Cria a execução vinculada sem materializar um registro de cliente. O integrador entrega a URL manualmente. |
| Misto | subject: {tax_id: "..."} ou subject: {full_name: "..."} (apenas um dos dois) | Aceito; tratado como shell. O campo isolado não dispara a materialização do cliente. Não é um erro. |
| Materializado | subject: {tax_id: "...", full_name: "...", ...} (ambos presentes) | Cria ou reutiliza um registro de cliente por tax_id e o vincula de volta à execução vinculada. Os demais campos do sujeito (email, phone, birth_date) preenchem o novo registro de cliente na criação; são ignorados quando o cliente já existe (consulte Semântica de reutilização do CPF). |
Independentemente do formato, notify.linked: true precisa de pelo menos um campo de contato no sujeito: phone, email ou os dois. Cada canal é ignorado quando o campo de destinatário correspondente está vazio, e o disparo só é interrompido antes de qualquer chamada ao provedor quando os dois estão ausentes; nesse caso, notify_sent é registrado como false. Um representante legal que só tem e-mail é notificado pela via de e-mail.
Para flow_type: kyc_minor, subject.relationship é obrigatório na criação da sessão, seja qual for o formato acima. Um shell (subject: {}) sem relationship é rejeitado; consulte Cardinalidade de kyc_minor.
Cardinalidade de kyc_minor
Seção intitulada “Cardinalidade de kyc_minor”Para flow_type: kyc_minor, o serviço exige exatamente uma entrada em linked_subjects com role: "representative". Qualquer um dos casos a seguir retorna 400 LINKED_SUBJECTS_REQUIRED:
- Campo
linked_subjectsausente linked_subjects: [](array vazio)- Mais de uma entrada
- Uma entrada com
rolediferente derepresentative
A entrada também precisa carregar subject.relationship. A ausência de subject.relationship retorna 422 LINKED_SUBJECT_RELATIONSHIP_REQUIRED. A relação é a fonte canônica do tipo de responsável consumido adiante pela trava do documento de vínculo e pelo salto automático da etapa de relação dentro da jornada, por isso precisa ser declarada logo no início, mesmo quando o restante do sujeito é um shell.
A criação shell continua explícita e possível; basta incluir relationship: envie linked_subjects: [{role: "representative", subject: {relationship: "mother"}}].
Para kyc_minor, duas validações adicionais se aplicam à entrada do representante legal:
linked_subjects[0].subject.relationshipé obrigatório. Omiti-lo retorna 422LINKED_SUBJECT_RELATIONSHIP_REQUIRED.- O
tax_iddo representante legal deve ser diferente dotax_iddo menor de idade. Reutilizar o mesmo CPF retorna 422CPF_MINOR_REPRESENTATIVE_MATCH.
Opt-in de notificação
Seção intitulada “Opt-in de notificação”O bloco notify controla se a própria Guardline entrega a URL, por SMS, e-mail ou WhatsApp.
| Campo | Tipo | Padrão | Descrição |
|---|---|---|---|
notify.primary | bool | false | Reservado. Atualmente não tem efeito; versões futuras vão disparar a URL do sujeito principal. |
notify.linked | bool | false | Quando true, a Guardline registra o opt-in e dispara a URL do representante legal quando o menor de idade conclui o módulo dele (não na criação da sessão). O plano padrão é SMS mais e-mail; o WhatsApp só é usado quando subject.notification_channel o indica pelo nome. O opt-in se aplica ao representante do kyc_minor e precisa de subject.phone, subject.email ou os dois. |
Momento do disparo
Seção intitulada “Momento do disparo”Para kyc_minor, o representante legal só é necessário depois que o menor de idade conclui a jornada. A Guardline persiste o opt-in de notify.linked no POST /sessions e dispara a mensagem nesse momento posterior (quando a execução pai passa para pending_representative), e não de forma síncrona durante a criação da sessão. Como resultado, o campo linked_subjects[i].notify_sent na resposta do POST /sessions é sempre false. O resultado real do disparo aparece depois em representative_notify_sent no GET /api/v1/link/{token} do token da execução pai (lido do registro da execução filha).
Seleção do canal
Seção intitulada “Seleção do canal”Quando o disparo ocorre, os canais são selecionados por:
linked_subjects[i].subject.notification_channel, quando presente:smsenvia apenas SMS,whatsappenvia apenas WhatsApp,email_smsenvia SMS e e-mail.- Caso contrário, vale o plano padrão, que é o mesmo conjunto de
email_sms: SMS e e-mail. O WhatsApp nunca faz parte do fallback e só é disparado quando é pedido pelo nome.
Cada via é então ignorada quando o campo de destinatário correspondente está vazio: SMS e WhatsApp precisam de subject.phone, e o e-mail precisa de subject.email.
Semântica de notify_sent
Seção intitulada “Semântica de notify_sent”O campo linked_subjects[i].notify_sent na resposta do POST /sessions é sempre false, porque o disparo é adiado para a conclusão do menor de idade (consulte Momento do disparo). Ele não indica se uma mensagem foi enviada.
Para observar o resultado real do disparo, leia representative_notify_sent em GET /api/v1/link/{token}:
true: pelo menos um canal tentado foi aceito pelo seu provedor. Para SMS e WhatsApp, isso significa que o provedor de mensageria retornou um identificador de mensagem; para e-mail, significa que o provedor de e-mail aceitou a mensagem para entrega.false:notify.linkedestava desligado, o sujeito não tinha nemphonenememail, ou todos os canais tentados foram recusados.
Um ID de mensagem do provedor não significa que a mensagem foi entregue ao destinatário. A confirmação assíncrona de entrega (callbacks de status do provedor) não é exposta nesta API; ela permanece nos registros de log de comunicação acessíveis pelo painel administrativo. Essa é uma escolha deliberada para manter a superfície da API honesta e enxuta.
Surface hint (modo incorporado)
Seção intitulada “Surface hint (modo incorporado)”Os integradores que renderizam a jornada dentro de um iframe definem surface_hint: "embed" na requisição. O backend persiste a flag na execução. Quando o iframe carrega /api/v1/link/{token} para renderizar a jornada, a resposta é sanitizada (CPF, email, phone e birth_date removidos do bloco do cliente; campos de URL do representante removidos do payload da execução pai). Consulte Comportamento da sanitização de PII.
O valor de surface_hint é interno ao servidor e não é devolvido em nenhuma resposta (nem em /sessions, nem em /link/{token}). Os integradores observam o efeito dele apenas pelo formato sanitizado do bloco customer em /link/{token}.
O endpoint legado GET /api/v1/embed/{token}, que funcionava como um resolvedor sanitizado separado, foi removido. Os integradores em modo incorporado agora usam /api/v1/link/{token} depois de criar a sessão com surface_hint: "embed".
Semântica de reutilização do CPF
Seção intitulada “Semântica de reutilização do CPF”Quando primary.tax_id corresponde a um registro ativo existente em customers_person, o serviço reutiliza o cliente existente em vez de criar um novo. Nesse caso, os campos primary.full_name, primary.email, primary.phone e primary.birth_date da requisição são ignorados; o cliente mantém os dados da primeira criação.
O mesmo vale para linked_subjects[i].subject.tax_id quando o CPF do sujeito vinculado corresponde a um registro existente.
Os dois campos não são tratados da mesma forma. primary.tax_id é normalizado antes da busca (consulte Identificação fiscal do sujeito principal), portanto a grafia não importa. linked_subjects[i].subject.tax_id não é validado nem normalizado: a correspondência é feita sobre a string exata enviada, então duas grafias do mesmo documento são duas chaves diferentes e produzem dois registros de cliente diferentes. Envie esse campo apenas com dígitos e sempre da mesma forma.
Motivo: idempotência. Repetir a chamada com o mesmo CPF não duplica registros de cliente. Contrapartida: os dados do cliente ficam presos à primeira criação; correções feitas em chamadas posteriores de POST /sessions não se propagam.
Para atualizar o nome, o e-mail ou o telefone de um cliente existente, use o backoffice ou os endpoints de gestão de clientes; POST /sessions não é o caminho para atualizações de cadastro. No desenvolvimento local, use um CPF novo a cada teste ou apague o registro existente antes de reutilizar o CPF.
Exemplos
Seção intitulada “Exemplos”KYC padrão, cold-start (sem dados do sujeito principal)
Seção intitulada “KYC padrão, cold-start (sem dados do sujeito principal)”{ "flow_type": "kyc", "channel": "web", "reference_id": "SOL-2026-00099"}O registro do cliente é materializado quando o usuário envia a etapa de documento na jornada. Até lá, customer_id fica ausente da resposta.
KYC padrão, dados completos do sujeito principal
Seção intitulada “KYC padrão, dados completos do sujeito principal”{ "flow_type": "kyc", "channel": "web", "reference_id": "SOL-2026-00099", "primary": { "full_name": "Carlos Oliveira", "tax_id": "12345678909", "email": "carlos@exemplo.com", "phone": "+5511999990000", "birth_date": "1990-05-15" }}KYC menor de idade com dados completos e notificação entregue pela Guardline
Seção intitulada “KYC menor de idade com dados completos e notificação entregue pela Guardline”{ "flow_type": "kyc_minor", "channel": "web", "reference_id": "SOL-2026-00042", "primary": { "full_name": "João Santos", "tax_id": "12345678909", "birth_date": "2010-05-15" }, "linked_subjects": [ { "role": "representative", "subject": { "full_name": "Maria Santos", "tax_id": "98765432100", "phone": "+5511999998888", "email": "maria@exemplo.com", "birth_date": "1985-03-20", "relationship": "mother", "notification_channel": "sms" } } ], "notify": { "primary": false, "linked": true }}A Guardline cria as duas execuções e registra o opt-in de notify.linked. O SMS para +5511999998888 contendo a URL do representante legal é disparado quando o menor de idade conclui o módulo dele, e não na criação da sessão; por isso, a resposta do POST /sessions retorna notify_sent: false (consulte Momento do disparo).
KYC menor de idade com representante shell (o integrador entrega o link)
Seção intitulada “KYC menor de idade com representante shell (o integrador entrega o link)”{ "flow_type": "kyc_minor", "reference_id": "SOL-2026-00043", "primary": { "full_name": "Ana Pereira", "tax_id": "11144477735", "birth_date": "2009-11-30" }, "linked_subjects": [ { "role": "representative", "subject": { "relationship": "mother" } } ]}A URL do representante legal é retornada na resposta (linked_subjects[0].url). O integrador a entrega pelo próprio canal. Nenhuma notificação é disparada (notify.linked assumiu o padrão false). O campo relationship é obrigatório no kyc_minor mesmo no formato shell; consulte Cardinalidade de kyc_minor.
Modo incorporado (renderização em iframe)
Seção intitulada “Modo incorporado (renderização em iframe)”{ "flow_type": "kyc", "surface_hint": "embed", "primary": { "full_name": "Lucas Almeida", "tax_id": "12345678909" }}A flag surface_hint da sessão controla a sanitização de PII em GET /api/v1/link/{token} quando o iframe resolve o token.
Resposta de sucesso (201)
Seção intitulada “Resposta de sucesso (201)”{ "error": false, "data": { "execution_id": "9b4771d5-ab60-4b5e-867d-573ed7cb684c", "customer_id": "155384e8-c86b-478e-be99-3937d2fee1ab", "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/abc123def456", "embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/abc123def456", "token": "<token>", "expires_at": "2026-05-13T12:49:34Z", "status": "created", "reference_id": "SOL-2026-00042", "linked_subjects": [ { "role": "representative", "execution_id": "c0aef12b-7c2a-4b3e-9a8d-f1e2d3c4b5a6", "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/def456ghi789", "embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/def456ghi789", "subject_status": "created", "notify_sent": false } ] }}| Campo | Descrição |
|---|---|
execution_id | Identificador único da execução do sujeito principal. |
customer_id | Identificador do registro do cliente. Ausente da resposta em cold-start (sem dados de primary enviados), porque ainda não existe registro de cliente; presente e estável assim que o registro existe. Teste a presença da chave, e não a igualdade com o UUID zerado: versões anteriores publicavam 00000000-0000-0000-0000-000000000000 aqui e os integradores liam esse valor como um identificador que seria preenchido depois, e é por isso que agora o campo é omitido. |
url | URL pública da jornada do sujeito principal. |
embed_url | URL que aponta para a rota compatível com iframe do sujeito principal. Use este valor como src do iframe ao renderizar dentro da aplicação do integrador. |
token | Slug do link, também aceito por GET /api/v1/link/{token}. |
expires_at | Expiração do link em ISO 8601. Momento da criação mais uma configuração válida para todo o deploy, 7 dias por padrão. Não é configurável por fluxo, portanto todos os links que uma instância emite carregam a mesma janela. |
status | Status inicial da execução (created). |
reference_id | Eco do reference_id enviado na requisição, quando informado. |
linked_subjects[] | Uma entrada por sujeito vinculado criado. Sempre presente (array vazio quando a requisição não declarou sujeitos vinculados). |
linked_subjects[i].role | Papel do sujeito vinculado (representative no kyc_minor, officer no kyb). |
linked_subjects[i].execution_id | ID da execução do sujeito vinculado. |
linked_subjects[i].url | URL da jornada do sujeito vinculado. |
linked_subjects[i].embed_url | URL compatível com iframe do sujeito vinculado. |
linked_subjects[i].subject_status | Status inicial do sujeito vinculado (created). |
linked_subjects[i].notify_sent | Sempre false nesta resposta. O disparo para o representante legal é adiado para a conclusão do menor de idade, portanto o resultado real é lido depois em representative_notify_sent no GET /api/v1/link/{token} (consulte Momento do disparo). |
O campo url é destinado ao acesso direto do usuário por SMS, WhatsApp, e-mail ou qualquer outro canal escolhido pelo integrador. O campo embed_url é destinado ao uso em iframe e webview. Ambos apontam para a mesma execução e resolvem para o mesmo conteúdo.
Nota: os campos
messagede erro são localizados porAccept-Language. Faça a correspondência porcodepara ramificações programáticas, não pelo texto da mensagem.
| HTTP | Código | Causa |
|---|---|---|
| 400 | INVALID_REQUEST | JSON malformado, campo desconhecido (a mensagem nomeia o campo) ou tipo de campo incorreto. |
| 400 | INVALID_IDEMPOTENCY_KEY | O header Idempotency-Key tem mais de 255 caracteres. |
| 400 | VALIDATION_FAILED | Falha de validação em nível de campo: nem flow_id nem flow_type foi enviado, flow_id não é um UUID, flow_id e flow_type divergem, o fluxo resolvido está inativo, channel fora do enum, relationship ou notification_channel fora do enum, birth_date do kyc_minor fora da faixa de 14 a 17 anos, linked_subjects[i].role inválido, entrada officer em um fluxo sem subfluxo de representante, biometric enviado para um fluxo sem a etapa de biometria, ou partner vazio ou com partner.email inválido. O envelope não indica qual dessas condições ocorreu; correlacione com o request_id. |
| 400 | INVALID_TAX_ID | primary.tax_id não é um CPF válido em um fluxo kyc ou kyc_minor, nem um CNPJ válido em um fluxo kyb. Consulte Identificação fiscal do sujeito principal. |
| 400 | FLOW_ID_REQUIRED | A requisição enviou flow_type sem flow_id, e a instância tem mais de um fluxo ativo desse tipo. Envie o flow_id lido em GET /flows. |
| 400 | LINKED_SUBJECTS_REQUIRED | Requisição kyc_minor com o array linked_subjects ausente, vazio ou acima do tamanho permitido, ou com um papel diferente de representative. |
| 400 | INVALID_FLOW_TYPE | O flow_type não é um entre kyc, kyb, kyc_minor, ou o flow_id resolve para um tipo de fluxo de uso interno (como kyc_representative). |
| 400 | MISSING_REQUIRED_FIELDS | Requisição kyc_minor sem primary.tax_id ou sem primary.full_name. |
| 422 | LINKED_SUBJECT_RELATIONSHIP_REQUIRED | Requisição kyc_minor cujo linked_subjects[0].subject.relationship está ausente ou vazio. Vale para todos os formatos de kyc_minor (shell, misto, materializado). |
| 401 | MISSING_TOKEN | Nem X-API-Key nem Authorization foi enviado, ou a credencial estava vazia. |
| 401 | INVALID_API_KEY | API Key inválida, revogada ou expirada. |
| 401 | INVALID_TOKEN | Apenas para chamadas com JWT: a credencial bearer não carrega o prefixo gl_live_/gl_test_, portanto é verificada como um JWT, e essa verificação falhou. |
| 403 | FORBIDDEN | Com JWT: o token não carrega nenhum papel autorizado, ou a conta está desativada. |
| 404 | FLOW_NOT_FOUND | O flow_id não existe. |
| 409 | ACTIVE_SESSION_EXISTS | Já existe uma sessão ativa para o mesmo sujeito principal e o mesmo fluxo. O corpo também carrega execution_id e resume_url da sessão que bloqueou esta, para que quem chama possa devolver a pessoa a ela em vez de criar outra. resume_url só está presente enquanto o link daquela sessão continua válido. |
| 422 | PHONE_POLICY_VIOLATION | Um fluxo mobile-only recebeu um número de telefone fixo em primary.phone ou linked_subjects[i].subject.phone. |
| 422 | CPF_MINOR_REPRESENTATIVE_MATCH | Requisição kyc_minor em que o tax_id do representante legal é igual ao tax_id do menor de idade. |
| 422 | REPRESENTATIVE_AGE_BELOW_MINIMUM | O birth_date do representante legal indica idade inferior a 18 anos. |
| 422 | LINKED_SUBJECT_RELATIONSHIP_REQUIRED | Requisição kyc_minor cuja entrada do representante legal omite subject.relationship. |
| 500 | INTERNAL_ERROR | Entre outras causas: o flow_type nomeia um tipo sem fluxo ativo para o integrador. A consulta retorna um erro puro de nenhuma linha encontrada que nada mapeia para um 4xx, portanto uma requisição que na verdade é um erro do cliente aparece como erro de servidor. Repetir não adianta; corrija o flow_type ou envie flow_id. |
Resolução do link
Seção intitulada “Resolução do link”GET /api/v1/link/{token}Endpoint público (sem autenticação). O frontend do integrador, o iframe ou qualquer navegador que abra a url da resposta da sessão chama este endpoint para carregar a jornada. Retorna a definição do fluxo, as etapas e o bloco customer.
Quando a sessão foi criada com surface_hint: "embed", a resposta é sanitizada (consulte Comportamento da sanitização de PII).
Limite de requisições: o limite é compartilhado por todo o namespace /api/v1/link/* (não por token) e é modulado pelo perfil de ambiente em que a API roda. O perfil padrão permite 10 requisições por hora por IP de origem; o perfil de desenvolvimento permite 120 requisições por minuto. O sandbox é servido a partir de um host de desenvolvimento, portanto o sandbox tem 120 por minuto e a produção tem 10 por hora. Só as resoluções que falham (resposta 400 ou acima) contam para o limite: resoluções bem-sucedidas não consomem o bucket, portanto vários links válidos resolvidos a partir do mesmo IP não o esgotam. Depois que um IP enche o bucket com falhas, porém, até um link válido vindo dele é recusado até a janela virar.
Resolver um token não o consome nem o rotaciona. O mesmo token continua podendo ser resolvido de novo até que um relógio dispare (a expiração do link ou a janela de sessão ativa descrita em Erros, abaixo).
Resposta (padrão)
Seção intitulada “Resposta (padrão)”{ "error": false, "data": { "execution_id": "9b4771d5-ab60-4b5e-867d-573ed7cb684c", "flow": { "id": "61000000-0000-0000-0000-000000000010", "type": "kyc_minor", "name": "KYC Menor", "layout_mode": "stepper", "cpf_match_mode": "block", "phone_policy": "mobile-only", "settings": {}, "steps": [ { "id": "...", "execution_step_id": "...", "step_type_code": "welcome", "step_type_name": "Boas-vindas", "step_order": 1, "title": "Boas-vindas", "config": {}, "is_required": true, "is_enabled": true, "status": "pending" } ] }, "customer": { "id": "155384e8-c86b-478e-be99-3937d2fee1ab", "name": "João Santos", "cpf": "12345678909", "cpf_masked": "***8909", "email": "joao@exemplo.com", "phone": "+5511999887766", "birth_date": "2010-05-15" }, "minor": null, "relationship": "mother", "status": "in_progress", "expires_at": "2026-05-13T12:49:34Z", "allowed_origins": ["https://cliente.com"], "representative_link": "https://acme.onp.prod.guardline.com.br/onboarding/link/def456ghi789", "representative_token": "<linked-token>", "representative_status": "created", "representative_notify_sent": true }}| Campo | Descrição |
|---|---|
execution_id | Identificador da execução do sujeito ao qual este token pertence. |
flow | Definição do fluxo: id, type, name, layout_mode, cpf_match_mode (alert ou block), phone_policy (any ou mobile-only), settings e os steps ordenados. Cada etapa carrega id, execution_step_id, step_type_code, step_type_name, step_order, title, description, config, is_required, is_enabled, status e (apenas na superfície padrão) display_data e completed_fields. |
customer | Cadastro do cliente. Campos abaixo. |
customer.id | UUID do cadastro do cliente. Em cold-start, e em toda execução KYB, é o UUID zero 00000000-0000-0000-0000-000000000000, com cpf_masked vazio. Observe a assimetria com POST /sessions, que omite customer_id na mesma situação: este payload sempre carrega o bloco customer, portanto a ausência de um cadastro aparece como zeros, e não como uma chave ausente. Verifique o UUID zero aqui e a presença da chave lá. |
customer.name | Nome completo. Vazio em um registro cold-start. |
customer.cpf | CPF sem máscara. Removido quando surface_hint=embed. |
customer.cpf_masked | CPF mascarado: três asteriscos seguidos dos quatro últimos dígitos (***8909). A chave está sempre presente, nunca é omitida, e não depende de cpf: ela carrega a string vazia quando não há cadastro de cliente a mascarar, o que acontece em cold-start e em toda execução KYB, e sobrevive a surface_hint=embed mesmo que o próprio cpf seja removido ali. |
customer.email | E-mail. Removido quando surface_hint=embed. |
customer.phone | Telefone em E.164. Removido quando surface_hint=embed. |
customer.birth_date | Data de nascimento em YYYY-MM-DD. Removido quando surface_hint=embed. |
minor | Quando este token pertence ao representante legal de um par kyc_minor, carrega o bloco de cliente do menor de idade (mesmo formato de customer); null caso contrário. Definido como null quando surface_hint=embed. A chave nunca é removida, portanto o payload carrega "minor": null em vez de omiti-la. |
relationship | Relação do representante legal com o menor de idade (mother, father, etc.). Presente apenas em tokens kyc_minor; omitido caso contrário. |
status | Status atual da execução. Consulte Ciclo de vida da execução. |
expires_at | Expiração do link. |
allowed_origins | Origens autorizadas a incorporar o iframe (CSP frame-ancestors). |
representative_link | URL da jornada de um filho vinculado. Presente sempre que esta execução não tem pai e tem pelo menos um filho, sem nenhuma verificação do tipo de fluxo: o resolvedor pega o filho criado mais recentemente na execução, qualquer que seja o papel dele. Removido quando surface_hint=embed. |
representative_token | O token de link bruto desse mesmo filho (o FE usa para redirecionamento). Mesma condição de presença de representative_link. Removido quando surface_hint=embed. |
representative_status | Status atual da execução desse filho, copiado literalmente do código de status do filho, portanto carrega um estado de execução de Ciclo de vida da execução e nada mais. Um representante legal recém-provisionado aparece como created, e não como pending: pending não faz parte do vocabulário de status e este campo nunca pode carregá-lo. Preservado sob surface_hint=embed para que o FE saiba se deve fazer polling. |
representative_notify_sent | true quando o disparo para o representante legal (executado na conclusão do menor de idade, consulte Momento do disparo) retornou um ID de mensagem do provedor; false quando notify.linked estava desligado ou nenhum canal estava acessível; ausente quando a execução tem pai ou não tem nenhum filho. Removido quando surface_hint=embed. |
Atenção: em um pai KYB com representantes declarados (officer), os campos representative_* expõem um deles. Os quatro dependem de uma única condição, a de que a execução não tenha pai, e a consulta por trás deles pega o filho criado mais recentemente, sem filtro de tipo de fluxo. A única exclusão são as jornadas dos beneficiários finais, que nunca saem por aqui: o link de um beneficiário vai apenas para o e-mail dele. Portanto, resolver o link público de um pai KYB retorna o representative_link de um representante legal declarado e o representative_token bruto dele, que é uma credencial válida para a jornada daquela pessoa. Quando uma empresa declara vários representantes, o payload nomeia exatamente um deles, o mais recente, e qual deles é pode mudar conforme novos representantes são adicionados. Trate o token de link de um pai KYB como algo que também alcança a jornada do representante.
Comportamento da sanitização de PII
Seção intitulada “Comportamento da sanitização de PII”Quando a sessão foi criada com surface_hint: "embed", o resolvedor aplica sanitização em processo antes de retornar o payload:
- Removidos:
customer.cpf,customer.email,customer.phone,customer.birth_date,minor(definido comonull),representative_link,representative_token,representative_notify_sent, todos osflow.steps[].display_dataeflow.steps[].completed_fields. - Preservados:
customer.id,customer.name,customer.cpf_masked, todo o blocoflow(id, type, name, layout_mode, cpf_match_mode, phone_policy, settings, steps),relationship,status,expires_at,allowed_origins,representative_status,execution_id.
Sessões criadas sem surface_hint (padrão) recebem o payload completo.
Nota: os campos
messagede erro são localizados porAccept-Language. Consulte Idioma das mensagens de erro.
| HTTP | Código | Causa |
|---|---|---|
| 404 | LINK_NOT_FOUND | O token não existe. |
| 410 | LINK_EXPIRED | Dois gatilhos, e o segundo dispara enquanto expires_at ainda está no futuro. Um: expires_at já passou. Dois: a execução está em in_progress e a janela de sessão ativa dela, contada a partir da primeira resolução do link, se esgotou. A janela de sessão só é aplicada em in_progress, portanto uma execução parada em created ou pending_representative nunca é recusada por ela e continua podendo ser resolvida até expires_at. |
| 429 | RATE_LIMITED | Limite do perfil de ambiente excedido pelas resoluções que falharam, compartilhado entre todos os tokens: 10 por hora por IP de origem no perfil padrão, 120 por minuto no perfil de desenvolvimento em que o sandbox roda. |
Reenvio do link do representante
Seção intitulada “Reenvio do link do representante”POST /api/v1/onboarding/executions/{execution_id}/resend-representative-linkRefaz o disparo de notificação para o representante legal de uma execução kyc_minor existente. Não cria uma nova execução de representante; reutiliza o filho vinculado existente e tenta a notificação novamente.
Autenticação
Seção intitulada “Autenticação”Este endpoint exige um JWT de admin ou supervisor obtido no backoffice da Guardline. O header X-API-Key sozinho não é suficiente. Chamadas sem um JWT válido retornam 401 MISSING_TOKEN.
O endpoint é destinado a fluxos administrativos de recuperação (a equipe de suporte do integrador detecta que o representante legal não recebeu o link e o operador clica em “reenviar” no backoffice). Não foi projetado para ser chamado por automações do integrador. Para disparos conduzidos pelo integrador, defina notify.linked: true no POST /sessions original e (se o link expirar) crie uma nova sessão.
Idempotência
Seção intitulada “Idempotência”O endpoint respeita o header Idempotency-Key; consulte Idempotência.
Limite máximo
Seção intitulada “Limite máximo”No máximo 3 reenvios por execução. A 4ª tentativa retorna 429 RATE_LIMITED.
O limite é um contador vitalício na execução, e não uma taxa dentro de uma janela de tempo. Ele não é reiniciado: uma vez registrados 3 reenvios, essa execução nunca mais pode ser reenviada, e esperar não adianta. Leia o 429 como “esta execução não tem mais reenvios”, e não como “tente mais tarde”.
Um reenvio que falha também consome um dos três. O contador é incrementado antes de o filho ser resolvido e antes de o disparo ser tentado, de propósito, para que um integrador não consiga ultrapassar o limite provocando erros. Uma chamada que responde 404 NO_RESENDABLE_CHILD ou que falha durante o disparo já queimou um slot, portanto um operador que esbarre nesses casos pode acabar com menos de três reenvios utilizáveis.
| HTTP | Código | Causa |
|---|---|---|
| 400 | INVALID_ID | O execution_id do path não é um UUID válido. |
| 400 | VALIDATION_FAILED | A chamada foi feita no tipo errado de execução: o fluxo não é kyc_minor (uma execução KYB ou KYC comum cai aqui, e não em um 404), ou a execução não tem representante legal vinculado. |
| 401 | MISSING_TOKEN | JWT não informado. |
| 403 | FORBIDDEN | O JWT não tem o papel admin ou supervisor. |
| 404 | EXECUTION_NOT_FOUND | A execução principal não existe. |
| 404 | NO_RESENDABLE_CHILD | Não há filho representante em estado não terminal para reenvio. O slot de reenvio já foi gasto quando isso ocorre. |
| 409 | EXECUTION_TERMINATED | A execução principal está em approved, rejected, cancelled ou expired. blocked não é verificado aqui. |
| 429 | RATE_LIMITED | Limite vitalício de 3 reenvios atingido. Não é reiniciado. |
| 502 | INTERNAL_ERROR | Qualquer outra falha: leitura da execução ou do fluxo, leitura ou incremento do contador, ou o próprio disparo da notificação. |
A verificação de estado terminal por trás do 409 é mais estreita do que a tabela do ciclo de vida. O reenvio recusa apenas approved, rejected, cancelled e expired; ele não trata blocked como terminal, embora Ciclo de vida da execução marque blocked como terminal. Um reenvio em uma execução blocked, portanto, passa pela barreira do 409 e prossegue.
Ciclo de vida da execução
Seção intitulada “Ciclo de vida da execução”Diagrama de estados
Seção intitulada “Diagrama de estados”Vale enunciar explicitamente duas regras que as transições acima codificam. O expires_at só se aplica enquanto a execução está em created ou in_progress: a consulta de expiração não seleciona nenhum outro status, e ela é a única coisa que grava expired em qualquer lugar. Um pai kyc_minor parado em pending_representative, portanto, nunca expira por esse relógio, por mais que espere; quando um filho expira, o worker emite um evento de vínculo no pai e deixa o status do próprio pai intacto. Uma empresa kyb que aguarda os beneficiários finais é a exceção: ela expira quando as rodadas de verificação deles se esgotam (consulte Beneficiários finais). E o estado created termina no envio da primeira etapa, e não na abertura do link: resolver o link apenas registra o início da sessão, portanto um usuário que abre o link e o abandona permanece em created.
Descrição dos estados
Seção intitulada “Descrição dos estados”| Estado | Terminal | Descrição |
|---|---|---|
created | Não | Sessão criada, nenhuma etapa enviada ainda. Abrir o link não faz sair deste estado; o envio da primeira etapa faz. |
in_progress | Não | O usuário está navegando pelas etapas da jornada. |
pending_representative | Não | Aguardando um ou mais representantes legais vinculados. Alcançado de duas formas: um pai kyc_minor cujo menor de idade concluiu a sua jornada, e uma empresa kyb que aguarda a verificação dos seus beneficiários finais (status_detail awaiting_beneficial_owners; consulte Beneficiários finais). Não interprete o estado como “o sujeito concluiu”. |
processing | Não | Todas as etapas enviadas; motor de decisão processando. |
pending_review | Não | Encaminhado para revisão manual. Não há prazo de resolução no contrato da API: um worker interno abre uma escalação em um caso que ficou aqui além de um limiar configurável, o que direciona atenção em vez de encerrar o caso. |
approved | Sim | Aprovado pelo motor ou por um analista. |
rejected | Sim | Reprovado pelo motor ou por um analista. |
completed | Sim | Todas as etapas concluídas sem decisão própria da execução, que é como um filho vinculado se encerra quando o veredito fica no pai: o representante legal do kyc_minor é o caso que os integradores encontram. Ele não carrega bloco decision. Consulte a execução filha vinculada, como este documento recomenda para kyc_minor, e este é o status que você recebe de volta. |
blocked | Sim | Jornada interrompida por uma regra de segurança (restrição de CPF, limite de tentativas de biometria). |
cancelled | Sim | Cancelado pelo integrador ou por um administrador. |
expired | Sim | Sessão expirada antes da conclusão. |
Subestado de vínculo para kyc_minor
Seção intitulada “Subestado de vínculo para kyc_minor”Para kyc_minor, a execução pai mantém o estado do ciclo de vida acima, enquanto o progresso do representante legal é acompanhado separadamente na execução filha vinculada. GET /api/v1/link/{token} no token do pai expõe o status do representante pelo campo representative_status.
O pai transita para pending_representative quando o menor de idade conclui sua jornada. Enquanto o representante legal cumpre sua parte, o pai permanece em pending_representative; a execução filha percorre created → in_progress → processing → terminal de forma independente. Quando o filho alcança um estado terminal positivo, a consolidação do pai é executada e o pai passa por processing até seu próprio estado terminal.
Detalhe do status
Seção intitulada “Detalhe do status”Toda execução carrega status_detail ao lado de status, na consulta e na listagem. Ele não cria nenhum status novo: um integrador que ignora o campo lê a execução exatamente como antes.
status | status_detail | Significado |
|---|---|---|
pending_review | in_analysis | Em análise, sem correção cadastral aberta. |
pending_review | awaiting_correction | Uma rodada de correção cadastral está aberta, aguardando o parceiro. Consulte Correção cadastral. |
pending_review | correction_submitted | O parceiro enviou a correção, que aguarda a análise. |
pending_review | correction_expired | O prazo da rodada passou sem envio, e a execução aguarda a análise. |
pending_representative | awaiting_beneficial_owners | Uma empresa kyb aguarda a verificação dos seus beneficiários finais. Consulte Beneficiários finais. |
pending_representative | pending_representative | Os demais casos, como o pai kyc_minor que aguarda o representante legal. |
| qualquer outro | o próprio status | Repete o status. |
Fora de pending_review e de pending_representative, o subestado é ignorado, portanto um subestado antigo nunca contradiz um status final.
Prazos e expiração
Seção intitulada “Prazos e expiração”Circula a ideia de que o link vale 24 horas, ou de que vale 7 dias. São três contadores distintos, cada um governando um status diferente, e o primeiro que vencer expira a execução.
| Relógio | Origem | Padrão | Só expira quando o status é |
|---|---|---|---|
link_expires_at | LINK_EXPIRATION_DAYS da instalação | 7 dias | created |
expires_at | expires_in_hours do próprio fluxo | 168 h, ou seja 7 dias | created ou in_progress |
| Sessão | SESSION_TIMEOUT_HOURS da instalação, contado a partir de started_at | 24 h | in_progress |
O que cada um significa na prática:
link_expires_atcobre o link que nunca foi aberto. Como só age sobrecreated, abrir o link uma vez já tira a execução do alcance desse relógio.expires_até o prazo da execução inteira, contado desde a criação e derivado do fluxo, não de variável de ambiente. É o único que continua valendo depois que a pessoa começou a responder.- O contador de sessão começa quando a pessoa abre o link pela primeira vez, e é ele que fecha a jornada abandonada no meio. É o relógio de 24 horas, e ele não é o prazo do link.
Consequência para a mensagem que o integrador mostra ao seu usuário: quem abandonou a jornada no meio perdeu a sessão de 24 horas, não o link de 7 dias, e são coisas diferentes de se dizer.
O primeiro é publicado na consulta da execução, como expires_at, então o integrador não precisa recalculá-lo.
Consulta de uma execução
Seção intitulada “Consulta de uma execução”GET /api/v1/onboarding/executions/{execution_id}Retorna o estado completo de uma execução, incluindo os dados coletados durante a jornada. Este endpoint é a principal fonte de dados para o integrador. Os webhooks notificam as transições de estado; os detalhes são obtidos por meio desta consulta.
Resposta do KYC padrão (200)
Seção intitulada “Resposta do KYC padrão (200)”{ "error": false, "data": { "execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "flow_type": "kyc", "reference_id": "SOL-2026-00099", "status": "approved", "status_detail": "approved", "decision": { "result": "approved", "risk_level": "low", "decided_at": "2026-03-26T15:30:00Z", "decided_by": "engine", "reasons": [] }, "customer": { "full_name": "Carlos Oliveira", "tax_id": "11144477735", "tax_id_masked": "***7735", "birth_date": "1990-05-15", "nationality": "Brasileira", "email": "carlos@exemplo.com", "phone": "+5511999990000", "income": "8500.00", "is_pep": false, "has_foreign_tax_obligation": false, "address": { "zip_code": "01310-100", "street": "Avenida Paulista", "number": "1000", "complement": "Sala 302", "neighborhood": "Bela Vista", "city": "São Paulo", "state": "SP" }, "document": { "type": "cnh", "status": "verified" }, "biometric": { "status": "verified" } }, "data_divergences": [], "links": { "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/abc123def456", "embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/abc123def456" }, "correction": null, "steps_completed": 8, "steps_total": 8, "created_at": "2026-03-26T14:00:00Z", "updated_at": "2026-03-26T14:12:00Z", "started_at": "2026-03-26T14:05:00Z", "completed_at": "2026-03-26T14:12:00Z", "expires_at": "2026-04-02T14:00:00Z" }}| Campo | Descrição |
|---|---|
execution_id | Identificador da execução. |
status | Status atual da execução. Consulte Ciclo de vida da execução. |
status_detail | Detalhe do status, sempre presente. Consulte Detalhe do status. |
flow_type | Tipo de jornada. |
reference_id | Eco do correlacionador do integrador, quando a sessão declarou um. |
steps_completed / steps_total | Progresso da jornada. |
decision | Bloco de decisão: result, risk_level, risk_score, decided_by, decided_at e reasons (consulte Motivos da decisão). Ausente até que exista uma decisão. risk_score é o score inteiro do qual risk_level deriva, omitido quando a execução não tem score (e então risk_level publica low). O result é normalizado a partir do código de decisão interno, e apenas os quatro códigos terminais são mapeados (auto_approved e manual_approved para approved, auto_rejected e manual_rejected para rejected); qualquer outro código é retornado como está. pending_review é, ele próprio, um código de decisão, portanto uma execução em revisão publica "result": "pending_review". Um código que ramifica apenas entre approved e rejected trata errado todos os casos em revisão. |
customer | Pessoa que é o sujeito da execução. Campos abaixo. |
data_divergences | Sempre presente como um array, vazio quando não há nenhuma divergência. Cada entrada reporta um campo cujo valor enviado durante a jornada divergiu do valor pré-preenchido: field, expected_masked, submitted_masked, step_id, recorded_at. Os valores são mascarados na inserção; os valores brutos nunca saem do backend. field é um entre name, email, phone, birth_date, cpf e identity_document: fluxos fora do Brasil registram a divergência do documento como identity_document, e fluxos brasileiros e linhas antigas continuam usando cpf. Trate os dois. |
correction | A rodada mais recente de correção cadastral, ou null quando a execução nunca teve uma. Consulte Correção cadastral. |
beneficial_owners | Presente apenas em uma empresa kyb cujo fluxo tem a cadeia de beneficiários finais habilitada. Consulte Beneficiários finais. |
created_at / started_at / completed_at | Timestamps da jornada. |
updated_at | A última alteração da execução ou da sua rodada de correção mais recente, a que for posterior. |
expires_at | O prazo da execução, derivado do expires_in_hours do fluxo. |
links | O link da jornada da execução, como { url, embed_url }: url é o link a enviar à pessoa e embed_url a sua variante para iframe, no mesmo formato de url e embed_url de POST /sessions, sempre refletindo o link atual da execução. O integrador pode contar com links.url para recuperar o link da jornada (por exemplo, para reenviá-lo) em vez de criar outra sessão. Diferente de POST /sessions, esta resposta não tem url, embed_url ou token no nível de cima: leia o link deste bloco. O bloco é omitido por inteiro quando a execução nunca recebeu um link. Ele é devolvido seja qual for o estado do link; se o link ainda abre depende da expiração dele e do status da execução. |
Bloco customer
Seção intitulada “Bloco customer”| Campo | Descrição |
|---|---|
full_name | Nome completo conforme coletado. |
tax_id | O documento de identidade da pessoa, bruto e sem formatação. Em jornadas brasileiras, é o CPF. |
tax_id_masked | Variante mascarada, mantida por compatibilidade retroativa. Em depreciação suave: prefira tax_id. Integrações novas devem ignorá-lo; as existentes continuam recebendo-o inalterado. Neste endpoint a máscara são três asteriscos seguidos dos últimos quatro caracteres, portanto 11144477735 é publicado como ***7735. Os dois endpoints não mascaram do mesmo jeito. A Listagem de execuções monta o mesmo campo com outra regra, que preserva os dígitos do meio e esconde as pontas, de modo que o valor da listagem expõe mais do documento do que o valor do detalhe. Nunca compare as duas strings mascaradas, e nunca faça parsing de nenhuma delas. |
birth_date, nationality, email, phone, income | Conforme coletados. phone carrega o prefixo de discagem sempre que a origem o carrega, de modo que um número coletado por uma jornada localizada é lido como E.164, e não como dígitos nacionais sem país. |
is_pep, has_foreign_tax_obligation | Sempre emitidos, inclusive quando false. |
address | zip_code, street, number, complement, neighborhood, city, state. |
document, biometric | Blocos de status de verificação: type e status. |
representative | Dados do representante legal aninhados dentro de customer. Um formato mais restrito do que o bloco acima, e não o mesmo: ele carrega full_name, tax_id_masked, birth_date, nationality, email, phone, income, is_pep, has_foreign_tax_obligation, address, document, biometric, além de relationship, guardianship_document e status. Não há tax_id bruto aqui; o identificador mascarado é a única forma do documento que você recebe. Reservado para compatibilidade retroativa; consulte a nota sobre kyc_minor abaixo. |
Resposta do KYC menor de idade (200)
Seção intitulada “Resposta do KYC menor de idade (200)”Em kyc_minor, o bloco customer da execução pai descreve o menor de idade. Os dados do representante legal são obtidos consultando separadamente a execução filha vinculada. Os dados de representative embutidos na resposta da execução pai são reservados para casos de compatibilidade retroativa e podem ser descontinuados em uma versão futura; novas integrações devem consultar explicitamente a execução filha vinculada usando o execution_id retornado em linked_subjects[].execution_id por POST /sessions.
O campo relationship no registro de cliente do representante legal aceita: mother, father, grandparent, legal_guardian, tutor, other. Quando o parentesco é diferente de mother ou father, a jornada do representante legal exige o upload de um documento de guarda; o status do upload é refletido em guardianship_document (null ou "uploaded").
O bloco decision só é preenchido quando a execução chega a um estado terminal (approved, rejected) ou está em revisão (pending_review). Em todos os demais estados, a chave está ausente da resposta, e não presente como null. O mesmo vale para todos os blocos opcionais deste payload (customer, address, document, biometric): teste a presença da chave. A única exceção deliberada é guardianship_document, que é emitido como um null explícito quando nenhum documento foi enviado, e data_divergences, que está sempre presente como um array.
Correção cadastral
Seção intitulada “Correção cadastral”Em instâncias com a correção cadastral habilitada, a análise pode pedir a correção de itens específicos de um cadastro em pending_review. A plataforma abre então uma rodada de correção: uma microjornada separada, que o parceiro preenche dentro do próprio portal. A execução original continua em pending_review durante toda a rodada, e é por ela que o integrador acompanha a correção. A execução da microjornada nunca aparece na listagem, e consultá-la pelo id responde como uma execução inexistente. A habilitação é por ambiente: onde a correção está desligada, correction é sempre null e status_detail em pending_review é sempre in_analysis.
Nenhum webhook acompanha a rodada. O integrador a segue pela leitura da execução original: status_detail muda a cada etapa da rodada, e updated_at avança sempre que a execução ou a sua rodada mais recente mudam, o que permite detectar a mudança por polling.
Nas instâncias que enviam essas notificações, quem foi indicado em partner.email na criação da sessão recebe um e-mail a cada momento da rodada: quando ela é pedida, enviada, retirada, expirada e aceita. O aviso de abertura informa apenas que há uma correção a enviar e o prazo; ele não traz link, itens nem mensagem, porque a correção se abre dentro do portal do parceiro. Sem partner.email, nenhum aviso é enviado, e o cliente nunca os recebe.
O bloco correction traz a rodada mais recente, na consulta e na listagem:
| Campo | Descrição |
|---|---|
id | Identificador da rodada. |
round | Número da rodada, a partir de 1. |
max_rounds | O teto de rodadas por execução vigente no momento da leitura. round pode superá-lo depois de o teto ser reduzido; nesse caso, nenhuma rodada nova abre. |
state | open (pedida, o parceiro ainda não a abriu), in_progress (aberta), submitted (enviada, aguardando a análise), accepted, rejected, expired (o prazo passou sem envio), withdrawn (o pedido foi retirado), cancelled ou closed_by_decision (encerrada por uma decisão sobre a execução). |
opened_at / due_at | Quando a rodada foi pedida e o prazo dela. due_at é o prazo da rodada, e não o expires_at da execução. |
submitted_at / closed_at | Envio e encerramento; null enquanto não ocorreram. |
close_reason | O próprio estado de encerramento (accepted, rejected, expired, withdrawn, cancelled ou closed_by_decision), ou null com a rodada aberta. Nunca é texto livre. |
items_count | Quantos itens a rodada pede. |
requires_customer | true quando algum item inclui a prova de vida, de modo que o cliente (ou o representante da empresa) precisa estar presente. Só é true quando a jornada tinha a prova de vida ligada ao abrir a rodada. |
links.embed_url | O endereço da microjornada, presente apenas enquanto a rodada aceita a entrada do parceiro (open ou in_progress). A microjornada abre somente incorporada no portal do parceiro, por isso não há url direta. |
Na consulta individual, o bloco traz ainda:
| Campo | Descrição |
|---|---|
message | A mensagem da análise ao parceiro, apenas quando foi escrita. |
items | Os itens pedidos: item_id, code e label do catálogo aprovado, reason (code, label e guidance), requires_customer, state (pending ou answered) e, em um esclarecimento ou em um documento complementar, question ou document com code e label. Um rótulo ausente do catálogo é publicado como o próprio código. |
previous_rounds | As rodadas anteriores, da mais recente para a mais antiga, nos campos da tabela acima sem max_rounds e sem links. Um array vazio na primeira rodada. |
Na listagem, cada item traz apenas o resumo, sem message, items e previous_rounds. O bloco nunca carrega a mesa, o analista, uma nota interna ou um motivo interno.
Beneficiários finais
Seção intitulada “Beneficiários finais”Em um fluxo kyb com a verificação dos beneficiários finais ligada, a empresa não é decidida ao final da própria jornada. A declaração de sócios é congelada, e cada beneficiário final dela (25% ou mais de participação efetiva, somando todos os caminhos pelo CPF) recebe uma jornada de KYC própria, com o link enviado ao e-mail declarado para ele. A empresa passa de processing para pending_representative, com status_detail awaiting_beneficial_owners, e só é decidida quando as verificações terminam; se as rodadas de um beneficiário se esgotam, ela expira. Uma declaração sem nenhum beneficiário final não estaciona: a empresa é decidida na hora.
O bloco beneficial_owners acompanha essa espera na consulta e nas linhas da listagem. Ele aparece em uma empresa kyb (execução raiz) cujo fluxo tem a cadeia de beneficiários habilitada a partir do momento em que a declaração de sócios é congelada, continua presente depois da espera e está ausente em qualquer outra execução. Traz apenas contagens, nunca um nome, um CPF ou um e-mail, sempre sobre a rodada atual de cada beneficiário:
| Campo | Descrição |
|---|---|
total | Todos os beneficiários finais da declaração congelada. |
sent | Os que ainda não abriram o link. |
in_progress | Os que estão preenchendo a jornada. |
in_review | Os que estão com o KYC na mesa de análise. |
approved / rejected | Os aprovados e os reprovados. |
expired / cancelled | Os que tiveram o prazo da rodada vencido e os que tiveram a jornada cancelada. |
max_rounds | Quantas rodadas cada beneficiário tem antes de a empresa expirar; 1 quando a verificação está desligada no fluxo. |
Os webhooks dessa espera saem na empresa e estão descritos em Eventos linkage.* (kyc_minor).
Motivos da decisão
Seção intitulada “Motivos da decisão”O array decision.reasons carrega o motivo canônico por trás da decisão. Ele está sempre presente como array: vazio ([]) nas decisões automáticas do motor e nas aprovações, e preenchido apenas quando uma decisão manual registrou um motivo do catálogo. Hoje ele traz no máximo uma entrada. Cada entrada tem um code legível por máquina e uma description legível por pessoas:
"decision": { "result": "rejected", "risk_level": "high", "decided_at": "2026-03-26T15:30:00Z", "decided_by": "analyst", "reasons": [ { "code": "pep_restriction", "description": "Restrição PEP" } ]}Valores possíveis de code:
| code | description |
|---|---|
invalid_document | Documento inválido |
fraud_suspected | Suspeita de fraude |
pep_restriction | Restrição PEP |
sanctions_match | Match em sanções |
biometric_failed | Biometria reprovada |
high_risk | Risco elevado |
underage | Menor de idade |
incomplete_docs | Documentação incompleta |
expired_document | Documento vencido |
data_inconsistency | Dados inconsistentes |
other | Outro |
Trate code como o identificador estável para o tratamento programático. A description é o nome do motivo no catálogo da instância, que pode ser ajustado por instância e ampliado com o tempo, portanto tolere códigos desconhecidos. Se o código não estiver no catálogo, a description repete o próprio code.
| HTTP | Código | Causa |
|---|---|---|
| 400 | INVALID_ID | O execution_id do path não é um UUID válido. |
| 401 | MISSING_TOKEN | Nenhuma credencial foi enviada, nem em X-API-Key nem em Authorization: Bearer. |
| 401 | INVALID_API_KEY | API key inválida, revogada ou expirada. |
| 404 | EXECUTION_NOT_FOUND | Nenhuma execução com esse execution_id. |
| 502 | INTERNAL_ERROR | Qualquer outra falha ao ler a execução ou o fluxo dela. |
O 404 é uma verificação de existência e nada mais. A leitura não carrega nenhum predicado de tenant, portanto ela não consegue informar que um id pertence a outra pessoa; o isolamento aqui é o banco de dados dedicado por instância descrito em Ambientes, e não um teste de propriedade em nível de linha.
Listagem de execuções
Seção intitulada “Listagem de execuções”GET /api/v1/onboarding/executionsRetorna uma lista paginada de execuções. Por padrão, ficam de fora da resposta as execuções em estado terminal (approved, rejected, blocked, expired, cancelled) e as que ainda estão no estado inicial created (criadas, mas sem nenhuma etapa enviada); informe um status explícito, com um ou mais códigos, para incluí-las (por exemplo, status=created retorna as linhas excluídas nesse estado). A exclusão vale com qualquer outro filtro, inclusive reference_id, tax_id e o período de criação: só um status explícito a desliga.
O filtro é a exclusão por status e nada mais: não há nenhuma verificação separada de se o link ou a sessão da linha continua válido, portanto uma linha cujo link expirou, mas cujo status ainda não foi encerrado, continua sendo retornada.
Os resultados são ordenados por created_at decrescente, com execution_id decrescente como critério de desempate determinístico entre linhas criadas no mesmo instante.
Parâmetros de consulta
Seção intitulada “Parâmetros de consulta”| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
page | inteiro | 1 | Página solicitada. |
per_page | inteiro | 20 | Itens por página (máximo: 100). Valores acima de 100 são limitados a 100. |
status | lista | (nenhum) | Um ou mais códigos de status, separados por vírgula (status=approved,rejected) ou com o parâmetro repetido (status=approved&status=rejected); um único código continua aceito. Os códigos aceitos são created, in_progress, processing, pending_review, pending_representative, approved, rejected, blocked, completed, cancelled, expired. Quando omitido, ficam de fora approved, rejected, blocked, expired, cancelled e created; qualquer status explícito, de um código ou de uma lista, desliga essa exclusão e retorna as linhas nos status pedidos, inclusive os seis. Qualquer outro valor, inclusive um vazio entre vírgulas, retorna 400 INVALID_REQUEST. |
created_from | data (AAAA-MM-DD) | (nenhum) | Início do período de criação (created_at), inclusivo, no horário de Brasília (UTC-3): conta a partir das 00:00 desse dia. Pode vir sem created_to. |
created_to | data (AAAA-MM-DD) | (nenhum) | Fim do período de criação, inclusivo, no horário de Brasília: conta até o fim desse dia. Pode vir sem created_from. Um created_from posterior a created_to retorna 400 INVALID_REQUEST. |
reference_id | string (≤ 255) | (nenhum) | Correspondência exata com o reference_id do integrador. A exclusão padrão continua valendo: sem status, uma execução em um dos seis estados excluídos não aparece, mesmo com o reference_id exato. Para ler uma execução cujo status você não sabe, consulte-a pelo id em Consulta de uma execução. |
flow_id | UUID | (nenhum) | Filtra por uma versão específica do fluxo. Um UUID malformado retorna 400 INVALID_REQUEST. |
flow_type | string | (nenhum) | Filtra pelo tipo de jornada: kyc, kyb ou kyc_minor. Qualquer outro valor, inclusive kyc_representative, retorna 400 INVALID_REQUEST. |
status_detail | lista | (nenhum) | Um ou mais entre in_analysis, awaiting_correction, correction_submitted e correction_expired, separados por vírgula ou com o parâmetro repetido. Mantém apenas as execuções em pending_review cujo status_detail é um deles, e combina com status: com um status diferente de pending_review, a lista volta vazia. awaiting_beneficial_owners não é aceito aqui. Qualquer outro valor, inclusive um vazio entre vírgulas, retorna 400 INVALID_REQUEST. |
correction_state | lista | (nenhum) | O estado da rodada de correção mais recente: um ou mais entre open, in_progress, submitted e expired, com as mesmas regras de status_detail. Uma execução sem rodada nunca corresponde. Combina com status e com status_detail. Qualquer outro valor retorna 400 INVALID_REQUEST. |
name | string (≤ 255) | (nenhum) | Correspondência por trecho do nome do cliente, sem diferenciar maiúsculas de minúsculas. Casa tanto com customers_person.name (kyc, kyc_minor, kyc_representative) quanto com customers_company.company_name (kyb). |
tax_id | string | (nenhum) | Correspondência exata com o documento do cliente: o CPF de uma pessoa ou o CNPJ de uma empresa. O valor é normalizado no servidor para apenas caracteres alfanuméricos, em maiúsculas, portanto 123.456.789-09 e 12345678909 são a mesma busca. Depois da normalização, ele precisa ser um CPF (11 dígitos) ou um CNPJ (14 caracteres, numérico ou alfanumérico, com os dois últimos numéricos); qualquer outro formato retorna 400 INVALID_REQUEST antes de a consulta ser executada. |
Os filtros são combináveis: ?reference_id=SOL-2026-00042&status=approved retorna as execuções aprovadas cujo reference_id corresponde exatamente; ?name=silva&status=in_progress retorna as execuções em andamento cujo nome do cliente contém silva.
O período e a lista de status restringem as linhas e o pagination.total da mesma forma. Um período sem status mantém a exclusão padrão: ?created_from=2026-09-01&created_to=2026-09-30 retorna as execuções criadas em setembro, menos as dos seis status omitidos.
Os cinco cadastros mais recentes de um conjunto de status, como os de um painel de últimos cadastros, saem de uma única chamada:
GET /api/v1/onboarding/executions?page=1&per_page=5&status=in_progress,processing,pending_review,pending_representative,approved,rejected,completedComo a listagem é ordenada por created_at decrescente, a primeira página traz os cinco criados mais recentemente entre esses status. Como o status é explícito, approved e rejected entram, mesmo estando entre os omitidos por padrão.
Resposta (200)
Seção intitulada “Resposta (200)”{ "error": false, "data": { "items": [ { "execution_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "flow_type": "kyc_minor", "reference_id": "SOL-2026-00042", "status": "in_progress", "status_detail": "in_progress", "steps_completed": 5, "steps_total": 9, "customer": { "full_name": "Marcus Vinicius Sales", "tax_id": "23117555854", "tax_id_masked": "***175558**", "email": "msales@exemplo.com.br", "is_pep": false, "has_foreign_tax_obligation": false }, "data_divergences": [], "correction": null, "created_at": "2026-03-31T09:00:00Z", "updated_at": "2026-03-31T09:41:00Z", "started_at": "2026-03-31T09:03:00Z", "expires_at": "2026-04-07T09:00:00Z", "links": { "url": "https://acme.onp.prod.guardline.com.br/onboarding/link/bcd234efg567", "embed_url": "https://acme.onp.prod.guardline.com.br/embed/t/bcd234efg567" }, "customer": { "full_name": "João Santos", "tax_id": "12345678909", "tax_id_masked": "***456789**", "email": "joao@exemplo.com" } } ], "pagination": { "page": 1, "per_page": 20, "total": 1, "total_pages": 1 } }}Os metadados de paginação ficam aninhados em data.pagination (page, per_page, total, total_pages). O bloco decision existe apenas no detalhe: obtenha-o pelo endpoint de consulta individual.
O bloco customer tem o mesmo formato na listagem e no endpoint de detalhe por id. Na listagem, só os campos de identificação de resumo vêm preenchidos (full_name, tax_id, tax_id_masked, email, is_pep, has_foreign_tax_obligation). Os sub-blocos de endereço, documento e biometria, mais o envelope decision, só são retornados por GET /api/v1/onboarding/executions/{execution_id}. O campo customer é omitido (em vez de vir como null) nas linhas em que ainda não há registro de cliente vinculado (por exemplo, sondagens ou sessões abandonadas antes do primeiro passo). Nas execuções kyb, tax_id é o documento da empresa (o CNPJ) e email é omitido (empresas não carregam e-mail no registro de cliente).
O campo tax_id é o documento bruto, sem máscara: em uma linha de pessoa, o documento de identidade (o CPF), em uma linha de empresa, o registro da empresa (o CNPJ). O tax_id_masked legado continua sendo emitido por compatibilidade retroativa e será removido em uma versão futura; integrações novas devem consumir tax_id.
Mais cinco campos viajam em todos os itens e passam facilmente despercebidos:
| Campo | Descrição |
|---|---|
status_detail | O mesmo detalhe do status da leitura de detalhe. Consulte Detalhe do status. |
correction | O resumo da rodada de correção mais recente, ou null. Consulte Correção cadastral. |
updated_at | A última alteração da execução ou da sua rodada de correção mais recente, a que for posterior. |
links | O mesmo bloco { url, embed_url } da leitura de detalhe, montado a partir do link da própria linha sem consulta extra, e omitido da mesma forma quando a linha nunca recebeu um link. links.url é a forma de recuperar o link da jornada de uma linha. |
data_divergences | Sempre emitido e, na listagem, é sempre o array vazio, seja qual for o que a execução registrou. As linhas de divergência por item seriam uma consulta por item, portanto a listagem não as busca. Leia as divergências reais no endpoint de detalhe; não interprete um [] aqui como “sem divergências”. |
O tax_id_masked de um item da listagem não é a máscara que o endpoint de detalhe produz. Aqui o valor mantém o meio do documento e esconde as pontas, portanto 12345678909 é publicado como ***456789**, contra ***7735 para o 11144477735 da leitura de detalhe. A máscara da listagem expõe mais dígitos do que a do detalhe. Trate as duas como strings de exibição do respectivo endpoint e não compare nenhuma delas.
As linhas de uma empresa kyb com a cadeia de beneficiários habilitada trazem também beneficial_owners, no mesmo formato da consulta (consulte Beneficiários finais).
Erros. Todos os filtros, exceto page e per_page, são validados antes de a consulta ser executada, e um valor recusado responde 400 INVALID_REQUEST. page e per_page são ajustados para valores seguros, em vez de rejeitados. Para status, created_from e created_to, as mensagens são as mesmas de Estatísticas de execuções.
| HTTP | Código | Causa |
|---|---|---|
| 400 | INVALID_REQUEST | status fora dos 11 códigos, created_from ou created_to fora do formato AAAA-MM-DD ou com uma data que não existe, created_from posterior a created_to, reference_id com mais de 255 caracteres, flow_id que não é um UUID, name com mais de 255 caracteres, tax_id inválido, flow_type fora de kyc, kyb e kyc_minor, ou status_detail ou correction_state fora dos valores aceitos. O code não indica qual deles ocorreu; o message nomeia o filtro, salvo com Accept-Language em espanhol, que recebe o texto genérico de INVALID_REQUEST. |
| 401 | MISSING_TOKEN | Nem X-API-Key nem Authorization foi enviado, ou a credencial estava vazia. |
| 401 | INVALID_API_KEY | API Key inválida, revogada ou expirada. |
| 502 | INTERNAL_ERROR | A própria listagem falhou. |
Estatísticas de execuções
Seção intitulada “Estatísticas de execuções”GET /api/v1/executions/statsRetorna contagens agregadas sobre todo o conjunto filtrado, sem o teto da paginação. Use-o para montar os indicadores de um painel sem paginar a listagem inteira e agregar do lado do integrador. Aceita a API key do integrador ou um JWT de backoffice.
Este endpoint conta todas as execuções, inclusive as terminais. Diferente de Listagem de execuções, ele não aplica nenhuma exclusão padrão: approved, rejected, blocked, expired, cancelled e created aparecem todos em by_status. O total conta as execuções em todos os status, enquanto a listagem pública GET /api/v1/onboarding/executions omite esses seis status quando status não é enviado, então o total dela pode ser menor. As execuções de correção cadastral ficam fora de todas as contagens, como ficam fora das listagens.
Parâmetros de consulta
Seção intitulada “Parâmetros de consulta”| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
flow_id | UUID | (nenhum) | Filtra pelo fluxo que originou as execuções. |
flow_type | string | (nenhum) | Filtra pelo tipo de jornada: kyc, kyb, kyc_minor, kyc_representative. |
status | lista | (nenhum) | Um ou mais códigos de status, separados por vírgula (status=approved,rejected) ou com o parâmetro repetido (status=approved&status=rejected); um único código continua aceito. Os códigos aceitos são created, in_progress, processing, pending_review, pending_representative, approved, rejected, blocked, completed, cancelled, expired. Qualquer outro valor retorna 400 INVALID_REQUEST. |
created_from | data (AAAA-MM-DD) | (nenhum) | Início do período de criação (created_at), inclusivo, no horário de Brasília (UTC-3): conta a partir das 00:00 desse dia. Pode vir sem created_to. |
created_to | data (AAAA-MM-DD) | (nenhum) | Fim do período de criação, inclusivo, no horário de Brasília: conta até o fim desse dia. Pode vir sem created_from. |
status_id | UUID | (nenhum) | Filtra pelo identificador do status. Prefira status. |
queue_id | UUID | (nenhum) | Filtra pela fila de análise. Tem precedência sobre queue. |
queue | string | (nenhum) | Filtra pelo nome da fila de análise, resolvido para queue_id no servidor. |
Os filtros são combináveis. status, created_from e created_to são validados: uma data fora do formato AAAA-MM-DD ou que não existe (como 2026-02-30 ou o ano 0000), um created_from posterior a created_to ou um status fora dos 11 códigos retornam 400 INVALID_REQUEST, com as mensagens listadas em Erros, abaixo. Os demais não são recusados: um UUID malformado em flow_id, status_id ou queue_id, e um nome de fila que não existe, são ignorados, e a resposta é 200 com as contagens sem aquele filtro; um flow_type desconhecido zera as contagens. A resposta sozinha não distingue um filtro aplicado de um ignorado, portanto confira os valores antes de enviar.
Cada mês de um gráfico mensal é uma chamada com o período daquele mês. Para setembro:
GET /api/v1/executions/stats?created_from=2026-09-01&created_to=2026-09-30O total e as contagens dessa resposta cobrem as execuções criadas de 1º a 30 de setembro, no horário de Brasília, com os dois dias incluídos.
Resposta (200)
Seção intitulada “Resposta (200)”{ "error": false, "data": { "total": 1228, "by_status": { "created": 9, "in_progress": 5, "processing": 1, "pending_review": 10, "pending_representative": 8, "approved": 204, "rejected": 24, "blocked": 0, "completed": 1, "cancelled": 250, "expired": 716 }, "by_risk": { "approve": 214, "review": 6, "block": 0 }, "by_type": { "kyc": 682, "kyb": 400, "kyc_minor": 74, "kyc_representative": 72 } }}total é a contagem sobre o conjunto filtrado e é igual à soma de by_status. by_type só tem chaves para os quatro tipos acima, portanto ele soma total apenas quando não há execuções de outro tipo: uma execução kyb_representative, por exemplo, entra em total e em by_status, mas em nenhuma chave de by_type.
by_risk não soma total, de propósito. Ele classifica apenas as execuções com decisão final de risco (approved, rejected, completed ou blocked) e com score de risco não nulo; as execuções em andamento, e as decididas sem score, ficam fora das três faixas. Os cortes vêm dos limites review_threshold e block_threshold da matriz de risco da instância, no domínio default:
| Faixa | Condição |
|---|---|
approve | score abaixo de review_threshold |
review | score maior ou igual a review_threshold e abaixo de block_threshold |
block | score maior ou igual a block_threshold |
Os cortes são lidos da matriz no momento da consulta, portanto uma contagem por faixa só é comparável ao longo do tempo se a matriz não foi recalibrada nesse intervalo: a recalibração não reescreve scores passados, apenas muda onde os cortes caem. Essas faixas também não são o risk_level da consulta de uma execução, que usa a régua fixa de 150, 350 e 700, então as duas classificações podem divergir para a mesma execução.
| HTTP | Código | Causa |
|---|---|---|
| 400 | INVALID_REQUEST | status com um código fora dos 11 aceitos, created_from ou created_to fora do formato AAAA-MM-DD ou com uma data que não existe, ou created_from posterior a created_to. |
| 401 | MISSING_TOKEN / INVALID_API_KEY | Nem X-API-Key nem Authorization foi enviado, ou a credencial é inválida. |
| 500 | INTERNAL_ERROR | A instância não tem matriz de risco configurada no domínio default, portanto as faixas não podem ser calculadas. O endpoint falha de forma explícita em vez de responder faixas zeradas, que se leriam como “nenhum risco em lugar nenhum”. Contate o suporte se isso acontecer. |
O message do 400 é um destes textos, salvo com Accept-Language em espanhol, que recebe o texto genérico de INVALID_REQUEST:
| Causa | message |
|---|---|
status fora dos 11 códigos | status inválido (valores aceitos: created, in_progress, pending_review, approved, rejected, cancelled, expired, processing, pending_representative, blocked, completed) |
created_from inválido | created_from inválido: use o formato AAAA-MM-DD com uma data existente |
created_to inválido | created_to inválido: use o formato AAAA-MM-DD com uma data existente |
created_from posterior a created_to | período inválido: created_from é posterior a created_to |
Cancelamento de uma execução
Seção intitulada “Cancelamento de uma execução”POST /api/v1/executions/{execution_id}/cancelTransiciona a execução para o estado cancelled. Quando a execução tem execuções filhas (o representante legal sob uma execução pai kyc_minor), a chamada é aplicada em cascata e as cancela na mesma operação.
Como o índice único parcial idx_executions_active_person_flow exclui os status terminais, o cancelamento libera o slot (customer_person_id, flow_id). Após um cancelamento bem-sucedido, um novo POST /onboarding/sessions para o mesmo CPF e fluxo deixa de retornar 409 ACTIVE_SESSION_EXISTS e uma nova sessão pode ser criada. Casos de uso típicos: reabrir uma jornada kyc_minor para novos testes, recuperar-se de um envio incorreto ou rotacionar uma sessão que o cliente final não conseguiu concluir a tempo.
Um evento de webhook onboarding.cancelled é emitido em caso de sucesso; consulte Famílias de eventos.
Autenticação
Seção intitulada “Autenticação”Aceita uma das duas opções:
X-API-Key(gl_live_*ougl_test_*); ouAuthorization: Bearer <JWT>(JWT do backoffice).
Idempotência
Seção intitulada “Idempotência”Respeita o header Idempotency-Key; consulte Idempotência.
Corpo da requisição
Seção intitulada “Corpo da requisição”O corpo é opcional. O único campo aceito é reason, registrado na trilha de auditoria e no payload do evento onboarding.cancelled:
{ "reason": "Duplicate registration, recreating with correct data"}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
reason | string (máx. 500) | Não | Motivo em texto livre registrado na auditoria e no payload do evento. |
Resposta de sucesso (200)
Seção intitulada “Resposta de sucesso (200)”{ "error": false, "data": { "execution_id": "dc8fc63f-f25a-4ce7-a9a7-2005bb3f1689", "cancelled_children": [ "b11d2b44-3a8e-4a0f-9c4d-90d1f8a2e111" ], "status_code": "cancelled", "cancelled_at": "2026-05-27T13:50:00Z" }}| Campo | Tipo | Descrição |
|---|---|---|
execution_id | UUID | Execução que foi cancelada. |
cancelled_children | array de UUID | UUIDs das execuções filhas canceladas em cascata (o representante legal sob um kyc_minor). A chave é omitida por completo, e não enviada como [], quando nada foi cancelado em cascata: nenhum filho, ou nenhum que ainda pudesse ser cancelado. Teste a presença da chave antes de ler o tamanho dela. |
status_code | string | Sempre cancelled em caso de sucesso. |
cancelled_at | ISO 8601 | Timestamp do cancelamento. |
| HTTP | Código | Causa |
|---|---|---|
| 400 | INVALID_ID | O UUID do path está malformado. |
| 400 | INVALID_REQUEST | O corpo da requisição está presente, mas malformado. |
| 400 | VALIDATION_FAILED | O reason tem mais de 500 caracteres. |
| 401 | MISSING_TOKEN / INVALID_API_KEY | Autenticação ausente, inválida ou expirada. |
| 404 | NOT_FOUND | Nenhuma execução com o UUID informado. |
| 409 | EXECUTION_TERMINAL | Execução já está em estado terminal. O conjunto de recusa é exatamente quatro códigos: approved, rejected, expired e cancelled. |
| 429 | RATE_LIMITED | Limite de requisições excedido. |
O cancelamento aceita um conjunto de estados mais amplo do que a coluna de terminais da tabela do ciclo de vida sugere. blocked é um deles: cancelar uma execução blocked funciona e responde 200, e não 409. O mesmo vale para cancelar uma execução em completed, e em processing, pending_review ou pending_representative. O conjunto aceito completo é created, in_progress, pending_review, pending_representative, processing, completed e blocked.
Webhooks
Seção intitulada “Webhooks”Webhooks são notificações HTTP enviadas pela Guardline ao backend do integrador quando uma execução muda de estado. Os payloads são intencionalmente enxutos: contêm os identificadores do evento e um pequeno bloco de dados. Os dados completos devem ser obtidos pelo endpoint de consulta.
Famílias de eventos
Seção intitulada “Famílias de eventos”Duas famílias coexistem após a reescrita:
onboarding.*: estado do onboarding como um todo. Emitidos em toda execução.linkage.*: estado de um sujeito vinculado dentro de um fluxo pareado. Abrange o representante legal dokyc_minor. Emitidos na execução pai, com o resultado do sujeito vinculado refletido no payload.
A mesa de conformidade emite uma terceira família, cmp.case.*, que é interna e não chega ao integrador. Ela está nomeada aqui apenas para que a ausência dela no seu endpoint não seja lida como entrega perdida.
Eventos onboarding.*
Seção intitulada “Eventos onboarding.*”| Evento | Descrição |
|---|---|
onboarding.started | Sessão criada via POST /sessions. |
onboarding.completed | Todas as etapas enviadas; motor de decisão em processamento. |
onboarding.approved | Aprovado pelo motor ou por um analista. |
onboarding.rejected | Reprovado pelo motor ou por um analista. |
onboarding.review | Encaminhado para revisão manual. |
onboarding.blocked | Jornada interrompida por uma regra de segurança. |
onboarding.expired | Sessão expirada antes da conclusão. |
onboarding.cancelled | Cancelado pelo integrador ou por um administrador. |
Eventos linkage.* (kyc_minor)
Seção intitulada “Eventos linkage.* (kyc_minor)”| Evento | Descrição |
|---|---|
linkage.pending | O menor de idade concluiu sua jornada e o pai passou para pending_representative. A URL do representante foi criada em POST /sessions; este evento marca a transição do pai, não a criação do filho. |
linkage.started | O representante legal acessou seu link e enviou a primeira etapa. |
linkage.completed | O representante legal concluiu sua jornada, o que dispara a consolidação do pai. O evento é emitido antes de as condições de consolidação serem avaliadas, portanto ele não garante que a consolidação foi executada: quando uma condição falha, não há consolidação. |
linkage.rejected | Um analista reprovou a execução do representante na revisão manual. |
linkage.expired | A execução do representante expirou antes da conclusão. |
Em uma empresa kyb que aguarda os beneficiários finais, os mesmos eventos saem na empresa. linkage.pending marca o estacionamento e traz data.beneficial_owners (total e pending). linkage.started, linkage.completed, linkage.rejected e linkage.expired acompanham cada beneficiário e trazem data.linked: execution_id da jornada do beneficiário, role (beneficial_owner), round e state. O onboarding.completed da empresa só sai quando ela é liberada para a decisão, e uma empresa cujas rodadas se esgotam recebe onboarding.expired.
Eventos legados representative.*
Seção intitulada “Eventos legados representative.*”A reescrita da API do integrador substituiu representative.{pending, started, completed} pela família linkage.* acima. Os tipos de evento legados não são emitidos nos novos caminhos de código, mas entregas históricas de webhook nos logs de auditoria do integrador podem referenciá-los. Trate as duas taxonomias como equivalentes em termos de informação para dados históricos; apenas linkage.* é emitido hoje.
Ordem de emissão
Seção intitulada “Ordem de emissão”Os eventos seguem uma ordem de emissão estrita dentro de cada execução. Por causa das retentativas, os eventos podem chegar fora de ordem ao endpoint do integrador. O campo event_sequence (crescente de forma monotônica por execução) deve ser usado como critério canônico de ordenação, e não o timestamp ou a ordem de entrega. Para reconhecer um evento repetido, por outro lado, use o event_id do corpo; consulte Deduplicação.
KYC padrão
Seção intitulada “KYC padrão”Aprovação automática:
onboarding.started (1) session createdonboarding.completed (2) all steps submittedonboarding.approved (3) approved by engineReprovação:
onboarding.started (1)onboarding.completed (2)onboarding.rejected (3)Revisão manual e, depois, resolução:
onboarding.started (1)onboarding.completed (2)onboarding.review (3)onboarding.approved (4) analyst decision (or onboarding.rejected)Bloqueado:
onboarding.started (1)onboarding.blocked (2)Expirado:
onboarding.started (1)onboarding.expired (2)KYC menor de idade
Seção intitulada “KYC menor de idade”Os eventos linkage.* referenciam o execution_id pai (do menor de idade), portanto o progresso do representante aparece na cadeia do pai. Isso não vale para todos os eventos que o pareamento produz: um filho representante emite os seus próprios onboarding.expired, onboarding.approved e onboarding.rejected no execution_id do filho.
Como o event_sequence é calculado por id de execução, esses eventos do filho caem em uma cadeia de sequência separada, que recomeça em 1. Um integrador que ordena por event_sequence e indexa pelo pai verá um evento avulso carregando um execution_id desconhecido e sequência 1. Esses payloads carregam parent_execution_id, que é o campo pelo qual correlacionar.
Aprovação automática:
onboarding.started (1) session createdlinkage.pending (2) minor completed; parent in pending_representativelinkage.started (3) representative accessed their linklinkage.completed (4) representative finishedonboarding.completed (5) consolidation; decision engine processingonboarding.approved (6) approved by engineReprovação:
onboarding.started (1)linkage.pending (2)linkage.started (3)linkage.completed (4)onboarding.completed (5)onboarding.rejected (6)Revisão manual e, depois, resolução:
onboarding.started (1)linkage.pending (2)linkage.started (3)linkage.completed (4)onboarding.completed (5)onboarding.review (6)onboarding.approved (7) analyst decision (or onboarding.rejected)Bloqueado durante o módulo do menor de idade:
onboarding.started (1)onboarding.blocked (2)Expirado aguardando o representante:
onboarding.started (1)linkage.pending (2)onboarding.expired (3) parent expiredlinkage.expired (4) representative's link expiredO onboarding.expired é despachado antes do linkage.expired, e não depois dele.
Representante reprovado pelo analista:
onboarding.started (1)linkage.pending (2)linkage.started (3)linkage.completed (4)onboarding.completed (5)onboarding.review (6)onboarding.rejected (7) parent decisionlinkage.rejected (8) analyst rejected the representativeComo na expiração, o onboarding.rejected é despachado antes do linkage.rejected.
Payload
Seção intitulada “Payload”{ "event": "onboarding.approved", "execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "flow_type": "kyc_minor", "reference_id": "SOL-2026-00042", "event_id": "7d3f2a9c-5b1e-4c8a-9f0d-2e6b4a8c1d35", "event_sequence": 6, "timestamp": "2026-04-01T10:45:00Z", "data": { "decision": { "result": "approved", "risk_level": "low", "decided_by": "engine", "decided_at": "2026-04-01T10:45:00Z" }, "liveness": { "status": "executed", "result": "passed" } }}| Campo | Descrição |
|---|---|
event | Tipo do evento. |
execution_id | Identificador da execução pai. Para eventos linkage.* em kyc_minor, é o ID da execução do menor de idade. O identificador do filho vinculado não vem no bloco data dos eventos de vínculo do kyc_minor; só os eventos dos beneficiários finais de uma empresa kyb o trazem, em data.linked.execution_id. Um consumidor que precise do id do filho tem que buscá-lo. |
flow_type | Tipo de jornada. |
reference_id | Identificador do integrador, conforme enviado na criação da sessão. |
event_id | UUID do evento, o mesmo valor do cabeçalho X-Guardline-Event-ID. Trafega dentro do corpo assinado, portanto a assinatura HMAC o cobre, e é idêntico em todas as retentativas da mesma entrega. É a chave de deduplicação preferida. As entregas anteriores à introdução do campo não trazem a chave; consulte Deduplicação. |
event_sequence | Número sequencial do evento dentro da execução, para ordenação canônica. Junto com execution_id, identifica o evento de forma única e é a chave de deduplicação alternativa quando falta event_id. |
timestamp | Timestamp de emissão do evento em ISO 8601. |
data | Bloco aditivo, ausente quando estaria vazio. Conteúdo abaixo. |
O bloco data
Seção intitulada “O bloco data”Tudo aqui é aditivo: uma chave simplesmente não aparece quando não há nada a reportar, então um receptor escrito para um envelope mais antigo continua funcionando.
| Chave | Quando está presente | Conteúdo |
|---|---|---|
data.decision | Assim que a execução tem uma decisão, e em vários eventos que não têm nenhuma | result, risk_level, risk_score, decided_by, decided_at. O result nem sempre contém uma decisão; veja abaixo. |
data.liveness | Nos eventos terminais, quando o fluxo tem etapa de prova de vida ou não | Resultado da prova de vida. Detalhado abaixo. |
Apesar do nome, data.decision nem sempre carrega uma decisão. Em vários eventos, o result carrega um status de execução: pending_representative, in_progress, completed, blocked, cancelled e expired aparecem ali. Esses mesmos eventos enviam risk_level: "low", que é um valor padrão, e não uma avaliação de risco real. Não ramifique o result apenas contra o vocabulário de decisões, e não leia risk_level como uma avaliação a menos que o result realmente contenha uma decisão.
Dois eventos são exceção ao formato acima: linkage.rejected e linkage.expired carregam apenas decision.
data.decision
Seção intitulada “data.decision”Presente nos eventos que carregam uma decisão ou uma transição de estado de vínculo (onboarding.approved/rejected/review/blocked e linkage.*).
| Campo | Descrição |
|---|---|
result | approved, rejected, pending_review, blocked, ou o estado do vínculo (pending_representative, in_progress, completed, expired). |
risk_level | Derivado do score da decisão por uma régua fixa: low (score até 150), medium (até 350), high (até 700), critical (acima de 700). Uma execução sem score publica low. |
risk_score | O score inteiro do qual risk_level deriva, nas decisões do motor e nas de analista. Omitido quando a execução nunca foi pontuada. |
decided_by | engine nas decisões do motor e analyst nas decisões de analista, no webhook e no endpoint de consulta. |
decided_at | Momento da decisão, em ISO 8601. |
data.liveness
Seção intitulada “data.liveness”Presente nos eventos terminais: onboarding.completed, onboarding.approved, onboarding.rejected, onboarding.review e onboarding.blocked emitidos pelo motor, os mesmos eventos quando a decisão é de um analista, e onboarding.expired. A etapa de prova de vida (iProov) é modular por fluxo: uma jornada pode executá-la, pulá-la por configuração ou pertencer a uma variante de fluxo sem ela, e o integrador pode ligá-la ou desligá-la por sessão com o campo biometric de POST /api/v1/onboarding/sessions. Este bloco permite distinguir “não executada por configuração” de “falhou”.
status | Significado |
|---|---|
executed | A prova de vida rodou e passou. A conclusão da etapa exige evidência aprovada, portanto executed implica aprovação; result: "passed" o acompanha. |
not_executed | O fluxo tem a etapa habilitada, mas a jornada terminou sem concluí-la (falha, limite de tentativas, abandono). |
skipped_by_config | A etapa existe no fluxo, mas foi desligada por configuração: desabilitada no fluxo, desabilitada nesta sessão ("biometric": false) ou uma condição do fluxo não atendida. |
not_configured | A variante do fluxo não tem etapa de prova de vida. |
O campo result só está presente quando status é executed, e hoje só assume passed. A semântica é de melhor esforço: se o bloco não puder ser resolvido no momento da emissão, ele é omitido, e a ausência não deve quebrar o parser do integrador.
Notas:
- A ausência do bloco significa “desconhecido”, nunca
not_configured. Envelopes emitidos antes deste contrato também não o trazem. Não deduza a configuração pela ausência do objetoliveness; “a jornada não tem prova de vida” é sinalizado exclusivamente pelo valor explícitonot_configured. onboarding.cancelleddeliberadamente não carrega o bloco: ele vale para os eventos terminais que carregam um desfecho, e um cancelamento não é um desfecho.
Cabeçalhos
Seção intitulada “Cabeçalhos”| Cabeçalho | Descrição |
|---|---|
Content-Type | Sempre application/json. |
X-Guardline-Event-ID | UUID estável do evento, idêntico em todas as tentativas do mesmo evento e sempre igual ao event_id do corpo. O cabeçalho continua sendo enviado, mas a assinatura não o cobre: faça a deduplicação pelo event_id do corpo assinado e use o cabeçalho para correlacionar logs. Consulte Deduplicação. |
X-Guardline-Delivery-ID | O identificador da linha de entrega. Ele é estável em todas as tentativas daquela entrega, e não único por tentativa: uma retentativa carrega o mesmo valor que a primeira tentativa carregou. Não faça a deduplicação por ele, ou toda retentativa será descartada em silêncio. |
X-Guardline-Attempt-Number | Número sequencial da tentativa (1 para a primeira entrega, 2 para a primeira retentativa e assim por diante). É o único discriminador por tentativa. |
X-Guardline-Timestamp | Timestamp Unix do momento da assinatura (apenas no modo hmac). |
X-Guardline-Signature | Assinatura HMAC-SHA256 em hexadecimal, feita com o segredo vigente (apenas no modo hmac). |
X-Guardline-Signatures | Lista separada por vírgula com uma assinatura por segredo válido, a do segredo vigente primeiro (apenas no modo hmac). Fora de uma rotação, tem um único item, igual a X-Guardline-Signature. Durante a janela de uma rotação, traz também a assinatura feita com o segredo anterior. Aceite a entrega se qualquer item conferir. Consulte Rotação de credenciais. |
Ocp-Apim-Subscription-Key | Chave de subscrição do API gateway. Enviada em todas as entregas no modo mtls_apim, em que é obrigatória. |
X-API-Key | Chave estática que o endpoint do integrador espera, quando há uma configurada no endpoint de webhook. Opcional e independente da assinatura HMAC. |
Deduplicação
Seção intitulada “Deduplicação”Cada evento carrega um UUID próprio, event_id, dentro do corpo da entrega, ao lado de event_sequence. É o mesmo valor que trafega no cabeçalho X-Guardline-Event-ID, permanece idêntico em todas as retentativas da mesma entrega e nunca se repete entre dois eventos. Como o que se assina é {timestamp}.{body}, a assinatura cobre o event_id; o cabeçalho fica fora do material assinado, então uma entrega reproduzida com o cabeçalho adulterado seria verificada como a original se o receptor tirasse a identidade do evento apenas dele.
Chaves de deduplicação, em ordem de preferência:
event_iddo corpo, autenticado pela assinatura. É a chave recomendada nos dois modos de autenticação: o campo é injetado no corpo de toda entrega, qualquer que seja o modo.execution_id+event_sequence, quando o corpo não trazevent_id. O par identifica um evento de forma única: a sequência é alocada por execução sob um bloqueio, é estável entre as retentativas da mesma entrega e nunca é reutilizada para dois eventos da mesma execução.
O cabeçalho X-Guardline-Event-ID continua sendo enviado e é sempre igual ao event_id do corpo. Ele serve para correlacionar logs e depurar; não o use como única chave de deduplicação.
As entregas anteriores à introdução do campo não trazem a chave event_id no corpo. O receptor deve tolerar a ausência e recorrer à segunda chave, em vez de rejeitar a entrega.
Não faça a deduplicação por X-Guardline-Delivery-ID: ele identifica a linha de entrega, não o evento, e descartaria toda retentativa em silêncio.
Autenticação de webhooks
Seção intitulada “Autenticação de webhooks”A Guardline suporta dois modos de autenticação para a entrega de webhooks, hmac e mtls_apim. O modo é configurado por endpoint de webhook, e uma restrição do banco de dados recusa qualquer outro valor. Não existe modo de entrega com token bearer ou OAuth: a Guardline nunca define um header Authorization em uma requisição de webhook.
Os dois modos são exclusivos, e não cumulativos. O hmac assina o corpo; o mtls_apim autentica na camada de transporte e não envia nenhum header de assinatura.
| Modo | Assinatura HMAC | Certificado de cliente | Chave de subscrição |
|---|---|---|---|
hmac (padrão) | Sim | Não | Não |
mtls_apim | Não | Sim | Sim |
Autenticação HMAC-SHA256
Seção intitulada “Autenticação HMAC-SHA256”A autenticação HMAC usa um segredo compartilhado provisionado durante a configuração do integrador no painel administrativo.
Processo de verificação no receptor:
- Extraia o cabeçalho
X-Guardline-Timestamp. - Rejeite se o timestamp for mais antigo que 5 minutos em relação ao relógio do servidor.
- Concatene o timestamp e o corpo da requisição:
{timestamp}.{body}. - Calcule o
HMAC-SHA256da concatenação usando o segredo compartilhado. - Codifique o resultado em hexadecimal.
- Compare com o cabeçalho
X-Guardline-Signatureusando comparação em tempo constante. - Rejeite se houver divergência.
O corpo assinado inclui o event_id do evento, portanto a mesma verificação autentica a identidade usada depois na deduplicação. O cabeçalho X-Guardline-Event-ID não faz parte do material assinado.
Pseudocódigo:
import hmac, hashlib, time
def verify_webhook(body: bytes, timestamp: str, signature: str, secret: str) -> bool: if abs(time.time() - int(timestamp)) > 300: return False
message = f"{timestamp}.".encode() + body expected = hmac.new( secret.encode(), message, hashlib.sha256 ).hexdigest()
return hmac.compare_digest(expected, signature)Autenticação mTLS
Seção intitulada “Autenticação mTLS”O TLS mútuo autentica a entrega na camada de transporte, em vez de assinar o corpo. O modo é voltado a um API gateway, e não a um listener HTTPS simples, e exige o cabeçalho Ocp-Apim-Subscription-Key descrito acima.
A configuração é do lado do operador. A Guardline não emite um certificado para o integrador confiar; ela apresenta o que lhe foi entregue:
- O operador faz o upload, pela API administrativa, do certificado de cliente e da chave privada que a Guardline deve apresentar.
- O operador faz o upload de um bundle de CA. Isso é opcional e, quando presente, é usado pela Guardline para verificar o certificado do servidor de destino. Ele não é distribuído ao integrador.
- O operador configura a chave de subscrição do gateway, que é obrigatória nesse modo.
- Em cada entrega, a Guardline apresenta o certificado de cliente enviado e manda a chave de subscrição em
Ocp-Apim-Subscription-Key. - O gateway valida o certificado contra o trust store com o qual foi configurado.
Nada na Guardline define ou verifica um CN, portanto não condicione a aceitação a esse campo: o certificado carrega o subject que o operador enviou. A identidade do par, do lado da Guardline, é a verificação comum de hostname contra a URL configurada.
Quando o mtls_apim está habilitado, os cabeçalhos X-Guardline-Signature e X-Guardline-Timestamp não são enviados.
Configuração do endpoint
Seção intitulada “Configuração do endpoint”O endpoint de webhook é configurado no painel administrativo da Guardline. Requisitos:
| Requisito | Detalhe |
|---|---|
| Protocolo | HTTPS obrigatório. |
| Versão do TLS | 1.2 ou superior. |
| Resposta de sucesso | Qualquer código 2xx (corpo ignorado). |
| Redirecionamentos | Não são seguidos. Um 301 ou 302 é recusado. |
| Cabeçalhos da resposta | Enviar a linha de status em até 5 segundos. |
| Timeout | Concluir a resposta em até 10 segundos. |
| Idempotência | Deduplicar as entregas usando o event_id do corpo assinado; se a entrega não o trouxer, usar execution_id + event_sequence. Consulte Deduplicação. |
Recomendação de implementação: receba o webhook, enfileire para processamento assíncrono e responda imediatamente com 200. Isso evita timeouts e garante entrega confiável.
Duas dessas linhas custam tempo às integrações, então vale enunciá-las com clareza.
O orçamento de resposta não é uma única janela de 10 segundos. O transporte também aplica um timeout de 5 segundos para os cabeçalhos da resposta, portanto um receptor que leva 7 segundos para enviar a linha de status falha, mesmo estando dentro dos 10 segundos documentados. Envie os cabeçalhos cedo e conclua o corpo depois.
Os redirecionamentos são recusados de imediato, e não seguidos. Um endpoint atrás de um 301 ou 302 nunca recebe a entrega. Pior: a recusa aparece como erro de transporte, e não como status HTTP, portanto conta como passível de retentativa e queima todo o orçamento de retentativas antes de a entrega ser esgotada. Configure a URL final.
Rotação de credenciais
Seção intitulada “Rotação de credenciais”Quando certificados mTLS ou o segredo HMAC são rotacionados, as retentativas em andamento de entregas já despachadas continuam usando as credenciais que estavam ativas no momento do despacho. Novas entregas passam a usar as credenciais atualizadas imediatamente.
A rotação do segredo HMAC não derruba o receptor que ainda não trocou o valor. O segredo anterior continua válido por uma janela de sobreposição, de 24 horas por padrão, e durante ela cada entrega leva em X-Guardline-Signatures uma assinatura com o segredo novo e outra com o anterior. X-Guardline-Signature continua sendo só a do segredo novo. Para trocar o segredo sem perder entregas, verifique contra todos os itens de X-Guardline-Signatures, publique o valor novo no receptor dentro da janela e só então peça à Guardline o encerramento do segredo anterior. Em caso de vazamento, a Guardline pode revogar o anterior na hora, o que interrompe qualquer receptor que ainda o use.
Um operador tem dois caminhos de reenvio, e eles não se comportam da mesma forma:
| Caminho | Credenciais usadas | Linha de entrega |
|---|---|---|
| Reenvio remontado a partir da configuração vigente | A configuração como ela está agora, portanto uma rotação é considerada. | Cria uma nova entrega. A original é mantida intacta para fins de auditoria. |
| Reenfileiramento da entrega existente | O snapshot de credenciais capturado quando a entrega foi enfileirada. | Reutiliza a mesma linha. |
A distinção importa depois de uma rotação: um operador que rotaciona um certificado e então reenfileira a entrega existente a reenvia com as credenciais revogadas, e ela falha de novo pelo mesmo motivo.
O caminho de remontagem tem uma consequência para o receptor. Como ele cria uma nova entrega, o reenvio carrega um novo id de evento e um novo número de sequência. Isso quebra as duas chaves nas quais este documento diz aos integradores para se apoiarem: o event_id (no corpo e no cabeçalho X-Guardline-Event-ID) deixa de corresponder ao original, portanto a deduplicação não vai reconhecer o reenvio como a repetição de um evento já processado, e o event_sequence novo não posiciona o evento onde ele pertence na cadeia da execução.
Política de retentativas
Seção intitulada “Política de retentativas”Quando uma entrega falha, a Guardline a repete com backoff exponencial. O intervalo abaixo é a espera depois que a tentativa numerada falha:
| Tentativa que falhou | Espera antes da próxima tentativa |
|---|---|
| 1 | 30 segundos |
| 2 | 2 minutos |
| 3 | 10 minutos |
| 4 | 30 minutos |
| 5 | 1 hora |
| 6 | 2 horas |
| 7 | 4 horas |
| 8 | 8 horas |
| 9 | 12 horas |
| 10 | Nenhuma. A entrega é marcada como exhausted. |
Todos os intervalos acima são nominais, por dois motivos.
Cada atraso agendado é multiplicado por um fator de jitter aleatório entre 0.8 e 1.2, portanto um intervalo nominal de 1 hora cai em algum ponto entre 48 e 72 minutos.
O despachante drena a fila em um tick de 30 segundos, e não de forma contínua. Uma entrega cujo atraso já se esgotou ainda espera pelo próximo tick, e o mesmo tick atrasa a primeira entrega de qualquer evento em até 30 segundos depois do próprio evento. Não trate um webhook como um sinal de tempo real.
Nem toda falha rende a escada acima. Ela se aplica a timeouts, erros de transporte, respostas 5xx e a exatamente dois códigos 4xx. Qualquer outro 4xx é tratado como não passível de retentativa e esgota a entrega na primeira tentativa, sem nenhuma retentativa. Isso cobre a falha real mais provável: um receptor que rejeita o envelope, uma chave de entrada rotacionada do lado do integrador ou uma rota que ainda não está no ar. Todos eles consomem a entrega imediatamente, em vez de deixar uma janela para corrigir o problema. As duas exceções 4xx que são repetidas são 408 Request Timeout e 429 Too Many Requests.
Após 10 tentativas sem sucesso, a entrega é marcada como exhausted e um alerta interno é gerado. As notificações continuam acessíveis pelo endpoint de polling (veja Polling de notificações). O reenvio manual também está disponível no painel administrativo.
Polling de notificações
Seção intitulada “Polling de notificações”O endpoint de polling é o mecanismo de fallback para garantir que nenhuma notificação se perca, independentemente de falhas de webhook. Ele também pode ser usado como mecanismo principal de integração por integradores que preferem um modelo pull.
O polling lê as mesmas entregas que o webhook envia, portanto herda a assinatura de eventos da configuração. Uma configuração de webhook pode assinar só alguns eventos; um evento fora da lista é descartado antes de virar entrega, e então não é enviado por webhook nem aparece aqui. Uma configuração sem lista assina todos os eventos, que é o comportamento de toda configuração anterior a esse recurso. Se você usa o polling como rede de segurança, confirme com a Guardline que a configuração assina todos os eventos que você trata.
Consulta de notificações pendentes
Seção intitulada “Consulta de notificações pendentes”GET /api/v1/onboarding/notificationsEsse endpoint usa paginação baseada em cursor.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
status | string | pending | Filtra por estado: pending, retrying, exhausted. Separados por vírgula para múltiplos valores. |
limit | integer | 50 | Itens por página (1 a 200; valores fora da faixa voltam para 50). |
after_cursor | string | (nenhum) | Cursor para a próxima página: o delivery_id codificado em base64 retornado em next_cursor pela chamada anterior. Leia o alerta sobre paginação abaixo antes de usá-lo. |
O failed é aceito pelo filtro, mas o despachante nunca atribui esse estado, portanto pedi-lo não retorna nada. O estado terminal de que um integrador realmente precisa é exhausted, que funciona.
A paginação por cursor só é confiável para a primeira página. A consulta ordena as linhas por data de criação decrescente, mas compara o cursor com a chave primária UUID aleatória. As duas ordenações não têm nada a ver uma com a outra, portanto toda página depois da primeira é um subconjunto arbitrário que ao mesmo tempo pula linhas e as repete. Não pagine em profundidade. Busque a primeira página, confirme o processamento do que você tratou e busque a primeira página de novo: as linhas confirmadas saem do resultado, portanto a fila se esvazia sem cursor.
Resposta (200):
{ "error": false, "data": { "items": [ { "delivery_id": "d4e5f6a7-b8c9-0123-def0-123456789abc", "execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "event": "onboarding.approved", "flow_type": "kyc_minor", "reference_id": "SOL-2026-00042", "event_sequence": 6, "status": "pending", "attempts": 3, "timestamp": "2026-04-01T10:45:00Z" } ], "next_cursor": null }}next_cursor é uma string base64 quando podem existir mais páginas (retornada quando a página está cheia), ou null quando não há mais itens. Devolva-o como after_cursor para buscar a próxima página, sujeito ao alerta acima. Cada item carrega flow_type e reference_id, a contagem atual de attempts e o timestamp da entrega.
O attempts é incrementado antecipadamente na falha, portanto ele corre um à frente das tentativas efetivamente feitas: uma linha que mostra 3 foi tentada duas vezes.
O last_attempt_at não é retornado. O campo vem do timestamp de entrega, que só é gravado em caso de sucesso, e este endpoint retorna apenas linhas não entregues, portanto ele está estruturalmente sempre ausente. Não escreva um consumidor que dependa dele.
A listagem não está restrita à configuração de webhook do integrador. Em uma instância que também executa a saída de gestão de casos, as entregas endereçadas àquele receptor interno aparecem na mesma listagem, carregando nomes de evento fora do catálogo documentado aqui. Filtre pelos nomes de evento que você reconhece e ignore o restante.
Confirmação de processamento
Seção intitulada “Confirmação de processamento”POST /api/v1/onboarding/notifications/{delivery_id}/ackMarca uma notificação como processada. Após a confirmação, a notificação deixa de aparecer nas consultas com status pending. A operação é idempotente: chamadas repetidas para o mesmo delivery_id retornam sucesso, sem efeitos colaterais.
Resposta (200):
{ "error": false, "data": { "delivery_id": "d4e5f6a7-b8c9-0123-def0-123456789abc", "status": "acknowledged" }}Diagramas de fluxo completos
Seção intitulada “Diagramas de fluxo completos”KYC padrão
Seção intitulada “KYC padrão”KYC menor de idade com notificação entregue pela Guardline
Seção intitulada “KYC menor de idade com notificação entregue pela Guardline”Referência de erros
Seção intitulada “Referência de erros”Códigos de status HTTP
Seção intitulada “Códigos de status HTTP”| Código | Significado | Orientação |
|---|---|---|
| 200 | Sucesso | Processe normalmente. |
| 201 | Recurso criado | Processe normalmente. |
| 400 | Requisição inválida | Corrija o payload. Não faça retry. |
| 401 | Falha na autenticação | Verifique a API Key (ou o JWT em resend-representative-link). Não faça retry. |
| 403 | Proibido | A credencial foi verificada, mas não carrega nenhum papel autorizado (JWT) ou nenhum escopo exigido (API key). Não faça retry. |
| 404 | Recurso não encontrado | Verifique os identificadores. Não faça retry. |
| 405 | Método não permitido | O path existe, mas não para este verbo. Corrija a chamada. Não faça retry. |
| 409 | Conflito | Trate conforme o código de erro específico. |
| 410 | Recurso não mais disponível (Gone) | O recurso era válido, mas não está mais acessível (por exemplo, link expirado). |
| 413 | Payload muito grande | O corpo excedeu o limite global de 13 MiB ou o limite da rota. Não faça retry. |
| 422 | Não processável | O corpo foi interpretado, mas viola uma regra de domínio. Corrija a requisição. Não faça retry. |
| 429 | Limite de requisições excedido | Dois limitadores diferentes respondem 429; veja abaixo. |
| 500 | Erro interno | Faça retry com backoff exponencial (máximo de 3 tentativas). |
| 502 | Erro interno durante o processamento | Faça retry com backoff exponencial. |
O 429 precisa ser separado, porque apenas uma das duas origens dele pode ser resolvida com espera. O limitador por IP da resolução do link envia um header Retry-After e se libera sozinho: aguarde o intervalo indicado e tente de novo. O limite de reenvios em POST .../resend-representative-link é um contador vitalício na execução, e não uma janela. Ele não envia Retry-After, nunca é reiniciado, e repetir nunca pode dar certo. Leia esse 429 como “esta execução não tem mais reenvios” e pare; consulte Limite máximo.
Códigos de erro de domínio
Seção intitulada “Códigos de erro de domínio”Nota: os campos
messagedos erros são localizados porAccept-Language(veja Idioma das mensagens de erro). Faça a correspondência pelocodepara ramificações programáticas.
| Código | HTTP | Descrição |
|---|---|---|
MISSING_TOKEN | 401 | Nenhuma credencial chegou ao handler: nem X-API-Key nem Authorization foi enviado, ou a credencial estava vazia. Este é o 401 comum em todo endpoint autenticado, inclusive nos que usam API key. Ele também cobre o JWT ausente em resend-representative-link. |
INVALID_API_KEY | 401 | API Key inválida, revogada ou expirada. |
INVALID_TOKEN | 401 | Um JWT Bearer foi enviado e falhou na verificação: assinatura inválida, emissor incorreto ou expirado. |
FORBIDDEN | 403 | O JWT não possui o role necessário (admin ou supervisor). |
FLOW_ID_REQUIRED | 400 | flow_type sem flow_id numa instância com mais de um fluxo ativo desse tipo. |
INVALID_REQUEST | 400 | JSON malformado, campo desconhecido ou tipo de campo incorreto. |
INVALID_ID | 400 | Um UUID do path está malformado. |
VALIDATION_FAILED | 400 | Falha na validação de campos, incluindo uma requisição que não envia nem flow_id nem flow_type. |
INVALID_TAX_ID | 400 | primary.tax_id não corresponde ao formato emitido pelo país do fluxo resolvido. |
INVALID_IDEMPOTENCY_KEY | 400 | Header Idempotency-Key com mais de 255 caracteres. |
LINKED_SUBJECTS_REQUIRED | 400 | Requisição kyc_minor com linked_subjects ausente, vazio ou acima do tamanho permitido, ou com role incorreto. |
INVALID_FLOW_TYPE | 400 | O flow_id resolve para um tipo de fluxo de uso interno. |
MISSING_REQUIRED_FIELDS | 400 | kyc_minor sem primary.tax_id ou primary.full_name. |
PHONE_POLICY_VIOLATION | 422 | Um fluxo mobile-only recebeu um número de telefone fixo. |
UNPROCESSABLE | 422 | Uma política de URL foi violada. |
CPF_MINOR_REPRESENTATIVE_MATCH | 422 | O CPF do representante legal é igual ao CPF do menor de idade. |
LINKED_SUBJECT_RELATIONSHIP_REQUIRED | 422 | Uma entrada kyc_minor omitiu linked_subjects[0].subject.relationship. |
REPRESENTATIVE_AGE_BELOW_MINIMUM | 422 | O birth_date do representante legal indica idade inferior a 18 anos. |
ACTIVE_SESSION_EXISTS | 409 | Já existe sessão ativa para o mesmo sujeito principal e fluxo. |
CUSTOMER_NOT_FOUND | 404 | Nenhum cadastro ativo para o id informado, nas rotas de sessão por id do cliente. Distinto de FLOW_NOT_FOUND, porque os dois podem faltar na mesma requisição. |
DUPLICATE | 409 | Uma restrição de unicidade recusou a escrita, normalmente duas chamadas concorrentes disputando a criação da mesma linha. |
EXECUTION_NOT_FOUND | 404 | Execução não encontrada. A leitura não carrega nenhum predicado de tenant, portanto esta é uma verificação de existência, e não um teste de propriedade; o isolamento é o banco de dados por instância (consulte Ambientes). |
NOT_FOUND | 404 | Ausência genérica. O cancelamento retorna este código, e não EXECUTION_NOT_FOUND, quando o id da execução não corresponde a nada. |
EXECUTION_TERMINAL | 409 | Foi solicitado o cancelamento de uma execução já em estado terminal. |
EXECUTION_TERMINATED | 409 | A execução principal chegou a um estado terminal (resend-representative-link). |
NO_RESENDABLE_CHILD | 404 | Não há filho representante para reenviar (apenas em resend-representative-link). |
LINK_NOT_FOUND | 404 | O token do link não existe. |
LINK_EXPIRED | 410 | Token do link expirado. |
FLOW_NOT_FOUND | 404 | Fluxo não encontrado. |
RATE_LIMITED | 429 | Limite de requisições excedido. |
PAYLOAD_TOO_LARGE | 413 | O corpo da requisição excedeu o limite global de 13 MiB, ou o limite da rota. A rota de envio de documento da jornada aceita um arquivo de até 8 MiB, cerca de 11,7 MiB de corpo depois da expansão do base64. Acima do limite global a conexão é recusada antes de chegar à API, portanto essa recusa não traz o envelope JSON de erro. |
ROUTE_NOT_FOUND | 404 | O path não corresponde a nenhuma rota registrada. |
METHOD_NOT_ALLOWED | 405 | O path existe, mas não aceita este verbo. |
INTERNAL_ERROR | 500 / 502 | O fallback genérico para uma falha não mapeada, levantado na maioria dos endpoints. 500 vindo do tratador de erros compartilhado; 502 onde um handler encapsula uma chamada downstream, como faz o endpoint de sessões. |
Resumo dos endpoints
Seção intitulada “Resumo dos endpoints”| Método | Path | Auth | Descrição |
|---|---|---|---|
| GET | /api/v1/onboarding/flows | API Key ou JWT | Lista as jornadas disponíveis. |
| POST | /api/v1/onboarding/sessions | API Key ou JWT | Cria uma sessão de onboarding (com sujeitos vinculados opcionais). |
| GET | /api/v1/link/{token} | Público (com limite de requisições) | Resolve um token de link para o fluxo + payload do cliente. Sanitizado quando a sessão foi criada com surface_hint=embed. |
| GET | /api/v1/onboarding/executions/{execution_id} | API Key ou JWT | Consulta a execução com os dados completos. |
| GET | /api/v1/onboarding/executions | API Key ou JWT | Lista as execuções ativas. |
| GET | /api/v1/executions/stats | API Key ou JWT | Contagens agregadas por status, faixa de risco e tipo de fluxo, sobre todo o conjunto filtrado. |
| POST | /api/v1/onboarding/executions/{execution_id}/resend-representative-link | JWT de Admin/Supervisor | Reenvia o link do representante pelos canais de notificação do fluxo. |
| POST | /api/v1/executions/{execution_id}/cancel | API Key ou JWT | Cancela uma execução (e o filho representante em kyc_minor), liberando o slot de sessão ativa para reuso. |
| GET | /api/v1/onboarding/notifications | API Key ou JWT | Faz polling das notificações de webhook pendentes. |
| POST | /api/v1/onboarding/notifications/{delivery_id}/ack | API Key ou JWT | Confirma o processamento de uma notificação de webhook. |
Versão 18, atualizada em