XIP

Documentos institucionais  /  Evidência de Registros de KYC

Evidência

Evidência de Registros de KYC

Demonstração estruturada dos registros de verificação de identidade mantidos pelo XIP, com dados mascarados, e da arquitetura de custódia dos documentos de identificação.

Documento EVD-01
Versão 1.0
Vigente desde 29/07/2026
Última revisão 29/07/2026
Classificação Público
Aviso essencial sobre os dados deste documento

Todos os registros exibidos aqui são sintéticos: foram construídos para este documento e não correspondem a pessoas reais nem a operações reais. Eles reproduzem fielmente a estrutura, os tipos, os estados e as relações da base de produção, mas nenhum dado de usuário real — ainda que mascarado — é publicado nesta página.

A evidência equivalente extraída do ambiente de produção, com dados reais mascarados, é fornecida a parceiros, auditores e autoridades mediante acordo de confidencialidade. Ver a seção final deste documento.

Objetivo e método

Este documento evidencia, de forma verificável, como o XIP registra e conserva o resultado da verificação de identidade de seus usuários e onde residem os documentos de identificação.

Ele responde a quatro perguntas que costumam ser formuladas em diligência de parceiros e de autoridades:

  1. Que informação de KYC o XIP efetivamente guarda, e em qual estrutura?
  2. Onde estão as imagens de documento e a imagem facial, se não estão no XIP?
  3. É verdade que um usuário não verificado não consegue transacionar? Como isso se comprova?
  4. Como um terceiro pode reproduzir essa verificação de forma independente?

O método adotado é a apresentação do esquema real das tabelas, de consultas reproduzíveis e de resultados dessas consultas sobre um conjunto sintético que cobre todos os estados possíveis do cadastro. As consultas são as mesmas aplicáveis à base de produção.

Arquitetura de custódia

A distinção a seguir é a chave para ler todas as evidências deste documento.

Item Onde reside Quem detém
Imagem do documento de identificação (frente e verso, ou CNH) Fora do XIP Prestador contratado — instituição autorizada
Imagem facial (selfie / prova de vida) Fora do XIP Prestador contratado
Tipo de documento apresentado Fora do XIP Prestador contratado
Número de telefone Fora do XIP Prestador contratado
Nome completo e CPF Base de dados do XIP — tabela receivers XIP e prestador contratado
Resultado da verificação, motivo da recusa e data da análise Base de dados do XIP — tabela receivers XIP
Identificador do cadastro no prestador Base de dados do XIP — tabela receivers XIP
Resposta bruta do prestador (trilha de auditoria) Base de dados do XIP — coluna raw_payload XIP

Ou seja: o XIP guarda o registro e o resultado da verificação; o prestador contratado guarda os arquivos que a fundamentaram. A correlação entre os dois lados é feita pelo identificador do cadastro, permitindo auditoria de ponta a ponta sem replicação de documentos.

Evidência A — Esquema da tabela de cadastros

Estrutura real da tabela receivers, que armazena o cadastro de verificação de cada usuário. Saída do comando de descrição de tabela do PostgreSQL:

xip=> \d receivers

                                       Table "public.receivers"
        Column         |            Type             | Nullable |              Default
-----------------------+-----------------------------+----------+-------------------------------
 id                    | bigint                      | not null | generated always as identity
 user_id               | bigint                      | not null |
 provider              | character varying(32)       | not null |
 gateway_receiver_id   | character varying(64)       | not null |
 document              | character varying(32)       | not null |
 name                  | character varying(255)      | not null |
 wallet_address        | character varying(64)       | not null |
 status                | character varying(16)       | not null |
 kyc_status            | character varying(16)       | not null |
 kyc_rejection_reason  | text                        |          |
 kyc_reviewed_at       | timestamp without time zone |          |
 raw_payload           | jsonb                       |          |
 created_at            | timestamp without time zone |          |
 updated_at            | timestamp without time zone |          |
 deleted_at            | timestamp without time zone |          |

Indexes:
    "receivers_pkey" PRIMARY KEY, btree (id)
    "receivers_user_id_provider_unique" UNIQUE CONSTRAINT, btree (user_id, provider)
    "receivers_provider_gateway_receiver_id_unique" UNIQUE CONSTRAINT, btree (provider, gateway_receiver_id)
    "receivers_user_id_index" btree (user_id)
    "receivers_provider_index" btree (provider)
    "receivers_status_index" btree (status)
    "receivers_kyc_status_index" btree (kyc_status)

Foreign-key constraints:
    "receivers_user_id_foreign" FOREIGN KEY (user_id) REFERENCES users(id)

O que o esquema comprova

Observação Consequência de controle
Não existe nenhuma coluna binária — nenhum bytea, blob, nenhum campo de caminho de arquivo ou URL de imagem É estruturalmente impossível que imagens de documento ou faciais estejam armazenadas nesta tabela. Não há onde guardá-las.
Restrição única em (user_id, provider) Um único cadastro de verificação por usuário e por prestador. Duplicidade é rejeitada pelo banco, não por convenção de código.
Restrição única em (provider, gateway_receiver_id) O identificador externo é unívoco: dois usuários não podem apontar para o mesmo cadastro no prestador.
Chave estrangeira user_id → users(id), não nula Todo cadastro de verificação está obrigatoriamente vinculado a uma conta. Não existe cadastro órfão nem anônimo.
Colunas kyc_status e status indexadas e não nulas O estado é sempre definido e consultável com eficiência — condição para a verificação de habilitação executada a cada operação.
Coluna kyc_reviewed_at Registra o momento da decisão, permitindo aferir tempestividade da análise.
Coluna raw_payload em jsonb Preserva a resposta íntegra do prestador, permitindo conciliação independente do que o XIP interpretou.
Coluna deleted_at (exclusão lógica) Registros não são fisicamente apagados pela aplicação, preservando a trilha para os prazos legais de conservação.

Evidência B — Regras de mascaramento aplicadas

As consultas de evidência aplicam as regras abaixo. Elas são determinísticas e não reversíveis: a partir do valor mascarado não é possível reconstituir o original.

Campo Regra Exemplo de saída
CPF (document) Preserva os 3 primeiros dígitos; mascara os 8 restantes, inclusive os dígitos verificadores 123.***.***-**
Nome (name) Preserva o primeiro nome; reduz cada sobrenome à inicial seguida de asteriscos MARIANA A*** S****
Endereço de carteira Preserva os 4 primeiros e os 4 últimos caracteres 7Xk2…f9Qa
Identificador no prestador Preserva o prefixo do tipo e os 4 últimos caracteres rcv_…a3f9
Nome do prestador (provider) Substituído por rótulo neutro nesta publicação psp-01
E-mail Preserva a primeira letra da parte local e a extensão do domínio m****@*****.com

Evidência C — Registros de cadastro

Consulta ao conjunto de demonstração, com mascaramento aplicado. O conjunto foi construído para cobrir todos os estados possíveis de verificação e de habilitação.

Bloco de identificação

xip=> SELECT id,
xip->        user_id,
xip->        provider,
xip->        mask_ext_id(gateway_receiver_id) AS gateway_receiver_id,
xip->        mask_cpf(document)               AS document,
xip->        mask_name(name)                  AS name,
xip->        mask_wallet(wallet_address)      AS wallet_address
xip->   FROM receivers
xip->  ORDER BY id;

 id | user_id | provider | gateway_receiver_id |    document    |         name         | wallet_address
----+---------+----------+---------------------+----------------+----------------------+----------------
  1 |    1041 | psp-01   | rcv_…a3f9           | 123.***.***-** | MARIANA A*** S****   | 7Xk2…f9Qa
  2 |    1042 | psp-01   | rcv_…b7c1           | 087.***.***-** | CARLOS E***** L****  | 9Fm4…2Rte
  3 |    1043 | psp-01   | rcv_…c2d8           | 341.***.***-** | JULIANA P***** M**** | 4Bqz…8Lkw
  4 |    1044 | psp-01   | rcv_…d9e3           | 512.***.***-** | RAFAEL T****        | Hn6v…3Yxs
  5 |    1045 | psp-01   | rcv_…e4f7           | 209.***.***-** | BEATRIZ O***** C***  | 2Kdp…7Mnb
  6 |    1046 | psp-01   | rcv_…f1a5           | 763.***.***-** | ANDRE L**** F*****   | Qw8r…5Zjt
  7 |    1047 | psp-01   | rcv_…a8b2           | 145.***.***-** | PATRICIA G***** N*** | Vc3y…9Hdl
  8 |    1048 | psp-01   | rcv_…b5c9           | 690.***.***-** | THIAGO R***** D***   | Ls7f…4Pqw
(8 rows)

Bloco de estado e decisão

xip=> SELECT id,
xip->        kyc_status,
xip->        status,
xip->        kyc_rejection_reason,
xip->        kyc_reviewed_at,
xip->        created_at
xip->   FROM receivers
xip->  ORDER BY id;

 id | kyc_status   | status    | kyc_rejection_reason                    | kyc_reviewed_at     | created_at
----+--------------+-----------+-----------------------------------------+---------------------+---------------------
  1 | APPROVED     | ACTIVE    |                                         | 2026-06-14 11:23:07 | 2026-06-14 10:58:41
  2 | APPROVED     | ACTIVE    |                                         | 2026-06-21 09:15:52 | 2026-06-21 08:47:19
  3 | UNDER_REVIEW | PENDING   |                                         |                     | 2026-07-27 16:02:33
  4 | SUBMITTED    | PENDING   |                                         |                     | 2026-07-28 19:44:10
  5 | REJECTED     | PENDING   | Documento ilegível: verso do RG fora    | 2026-07-22 14:31:26 | 2026-07-22 13:55:08
    |              |           | de foco. Reenviar com melhor iluminação |                     |
  6 | APPROVED     | SUSPENDED |                                         | 2026-05-30 10:07:44 | 2026-05-30 09:39:15
  7 | APPROVED     | BLOCKED   |                                         | 2026-04-18 15:52:31 | 2026-04-18 15:20:02
  8 | PENDING      | PENDING   |                                         |                     | 2026-07-29 08:11:57
(8 rows)

Leitura das linhas relevantes:

  • Linhas 1 e 2 — únicos cadastros habilitados a transacionar: verificação aprovada e cadastro ativo.
  • Linhas 3 e 4 — em curso; a data de análise está vazia porque não houve decisão.
  • Linha 5 — recusado com motivo registrado e legível, exibido ao usuário para correção e reapresentação.
  • Linha 6 — verificação aprovada, mas cadastro suspenso: não transaciona. Demonstra que a aprovação documental não é autorização permanente.
  • Linha 7 — verificação aprovada, cadastro bloqueado: não transaciona.
  • Linha 8 — cadastro criado, documentos ainda não enviados.

Evidência D — Distribuição de estados

Consulta agregada que permite aferir, a qualquer momento, a composição da base por estado — indicador de acompanhamento previsto na Política de KYC e EDD:

xip=> SELECT kyc_status,
xip->        status,
xip->        count(*) AS cadastros,
xip->        bool_and(kyc_status = 'APPROVED' AND status = 'ACTIVE') AS habilitado
xip->   FROM receivers
xip->  GROUP BY kyc_status, status
xip->  ORDER BY kyc_status, status;

 kyc_status   | status    | cadastros | habilitado
--------------+-----------+-----------+------------
 APPROVED     | ACTIVE    |         2 | t
 APPROVED     | BLOCKED   |         1 | f
 APPROVED     | SUSPENDED |         1 | f
 PENDING      | PENDING   |         1 | f
 REJECTED     | PENDING   |         1 | f
 SUBMITTED    | PENDING   |         1 | f
 UNDER_REVIEW | PENDING   |         1 | f
(7 rows)

Dos 8 cadastros, apenas 2 satisfazem a condição de habilitação. Os demais 6 estão impedidos de transacionar, cada um por um motivo distinto e registrado.

Evidência E — Trilha de auditoria do prestador

A coluna raw_payload conserva a resposta íntegra recebida do prestador contratado a cada evento do ciclo de vida do cadastro — criação, envio de documentos, atualização e consulta. Isso permite conciliar de forma independente o que o prestador afirmou e o que o XIP registrou.

Exemplo do conteúdo conservado para o cadastro recusado (linha 5), com mascaramento aplicado:

xip=> SELECT jsonb_pretty(mask_payload(raw_payload)) FROM receivers WHERE id = 5;

{
    "id": "rcv_…e4f7",
    "status": "PENDING",
    "kycStatus": "REJECTED",
    "kycRejectionReason": "Documento ilegível: verso do RG fora de foco. Reenviar com melhor iluminação",
    "name": "BEATRIZ O***** C***",
    "document": "209.***.***-**",
    "email": "b****@*****.com",
    "walletAddress": "2Kdp…7Mnb"
}
Note o que não está presente

A resposta do prestador contratado não devolve, e portanto o XIP não conserva, qualquer representação das imagens enviadas — nem conteúdo, nem endereço, nem identificador de arquivo. Ela devolve apenas o resultado da análise e os dados de identificação já conhecidos.

Evidência F — Registro de eventos recebidos

Eventos assíncronos comunicados pelo prestador contratado são registrados em tabela própria, com controle de unicidade e registro de erro de processamento. Estrutura:

xip=> \d webhook_events

                                    Table "public.webhook_events"
      Column       |            Type             | Nullable |              Default
-------------------+-----------------------------+----------+-------------------------------
 id                | bigint                      | not null | generated always as identity
 provider          | character varying(32)       | not null |
 event_id          | character varying(128)      | not null |
 event_type        | character varying(64)       | not null |
 external_ref      | character varying(64)       |          |
 transaction_id    | bigint                      |          |
 received_at       | timestamp without time zone | not null |
 processed_at      | timestamp without time zone |          |
 processing_error  | text                        |          |
 raw_payload       | jsonb                       |          |
 created_at        | timestamp without time zone |          |
 updated_at        | timestamp without time zone |          |

Indexes:
    "webhook_events_pkey" PRIMARY KEY, btree (id)
    "webhook_events_provider_event_id_unique" UNIQUE CONSTRAINT, btree (provider, event_id)
    "webhook_events_provider_external_ref_index" btree (provider, external_ref)
    "webhook_events_transaction_id_index" btree (transaction_id)

Controles evidenciados por esta estrutura:

  • a restrição única em (provider, event_id) torna o processamento idempotente: o mesmo evento não pode ser aplicado duas vezes, ainda que retransmitido;
  • received_at é obrigatório e processed_at é opcional, o que preserva o registro de eventos recebidos e não processados — uma falha não apaga a evidência de que o evento chegou;
  • processing_error conserva a causa da falha, permitindo apuração posterior;
  • raw_payload conserva a mensagem original recebida.

Adicionalmente, e antes de qualquer registro, toda notificação recebida é submetida a verificação de assinatura HMAC-SHA256 sobre o corpo bruto da mensagem. Notificações sem assinatura válida são rejeitadas, e não existe configuração que permita aceitar mensagem não assinada.

Evidência G — Bloqueio de operação sem habilitação

Esta é a evidência mais consequente do documento: a demonstração de que o impedimento de transacionar é estrutural, e não uma conferência manual sujeita a falha.

G.1 — Condição no código

A habilitação é definida no modelo de dados, em quatro métodos, e é a mesma para entrada e para saída:

// app/models/receiver.go

// IsApproved reports whether the gateway approved the user's KYC.
func (r *Receiver) IsApproved() bool { return r.KycStatus == KycStatusApproved }

// IsActive reports whether the receiver is enabled to transact.
func (r *Receiver) IsActive() bool { return r.Status == ReceiverStatusActive }

// CanPayin reports whether the receiver may originate a payin (approved + active).
func (r *Receiver) CanPayin() bool { return r.IsApproved() && r.IsActive() }

// CanPayout reports whether the receiver may originate a payout. The gateway
// applies the same eligibility as payin: KYC approved and receiver active.
func (r *Receiver) CanPayout() bool { return r.IsApproved() && r.IsActive() }

G.2 — Aplicação da condição em cada operação

Os serviços de entrada e de saída resolvem o cadastro do usuário e recusam a operação antes de qualquer efeito externo — sem gerar cotação e sem chamar o prestador contratado:

// app/services/client/payin/payin_service.go
if rec == nil || !rec.CanPayin() {
    return nil, ErrKycRequired
}

// app/services/client/payout/payout_service.go
if rec == nil || !rec.CanPayout() {
    return nil, ErrKycRequired
}

Observações relevantes:

  • a ausência de cadastro (rec == nil) é tratada como falha da condição, do mesmo modo que um cadastro recusado ou suspenso — não há caminho implícito de permissão;
  • a verificação está na camada de serviço do servidor, não no aplicativo: modificar o cliente ou construir requisições manualmente não a contorna;
  • o critério é literalmente o mesmo para entrada e saída, sem diferenciação por valor ou por antiguidade da conta.

G.3 — Comprovação sobre os dados

Consulta que confronta o estado de cada cadastro com o volume de operações efetivamente registradas para o usuário:

xip=> SELECT r.id,
xip->        r.kyc_status,
xip->        r.status,
xip->        (r.kyc_status = 'APPROVED' AND r.status = 'ACTIVE') AS habilitado,
xip->        count(t.id) AS operacoes
xip->   FROM receivers r
xip->   LEFT JOIN transactions t ON t.user_id = r.user_id
xip->  GROUP BY r.id, r.kyc_status, r.status
xip->  ORDER BY r.id;

 id | kyc_status   | status    | habilitado | operacoes
----+--------------+-----------+------------+-----------
  1 | APPROVED     | ACTIVE    | t          |        14
  2 | APPROVED     | ACTIVE    | t          |         3
  3 | UNDER_REVIEW | PENDING   | f          |         0
  4 | SUBMITTED    | PENDING   | f          |         0
  5 | REJECTED     | PENDING   | f          |         0
  6 | APPROVED     | SUSPENDED | f          |         0
  7 | APPROVED     | BLOCKED   | f          |         0
  8 | PENDING      | PENDING   | f          |         0
(8 rows)

O resultado é categórico: toda linha com habilitado = f apresenta zero operações. Nenhuma exceção. A consulta abaixo formaliza a asserção e deve retornar sempre vazio:

xip=> -- Deve retornar 0 linhas. Qualquer linha aqui é uma violação de controle.
xip=> SELECT t.id, t.user_id, t.type, t.status, r.kyc_status, r.status
xip->   FROM transactions t
xip->   JOIN receivers r ON r.user_id = t.user_id
xip->  WHERE NOT (r.kyc_status = 'APPROVED' AND r.status = 'ACTIVE');

 id | user_id | type | status | kyc_status | status
----+---------+------+--------+------------+--------
(0 rows)

Note ainda que a linha 6 — verificação aprovada, cadastro suspenso — registra zero operações, o que evidencia que a suspensão tem efeito imediato e que a aprovação documental anterior não confere autorização remanescente.

Evidência H — Ausência de persistência de imagens

A afirmação de que o XIP não armazena imagens de documento nem imagens faciais é verificável por três meios independentes, todos reproduzíveis.

H.1 — Nenhuma coluna capaz de armazenar imagem

Consulta ao catálogo do banco de dados, que enumera toda coluna de tipo binário ou com nome sugestivo de arquivo em todo o esquema:

xip=> SELECT table_name, column_name, data_type
xip->   FROM information_schema.columns
xip->  WHERE table_schema = 'public'
xip->    AND (data_type IN ('bytea', 'blob')
xip->         OR column_name ~* '(selfie|document_front|document_back|document_photo|image|photo|file|attachment|upload)')
xip->  ORDER BY table_name, column_name;

 table_name | column_name | data_type
------------+-------------+-----------
(0 rows)

Nenhuma coluna em todo o esquema é capaz de armazenar uma imagem, e nenhuma referencia arquivo de documento.

H.2 — Nenhuma gravação em armazenamento de arquivos

Busca no código-fonte por qualquer uso da camada de armazenamento de arquivos da aplicação:

$ grep -rn "facades.Storage()" app/ --include="*.go"
$ echo "resultado: nenhuma ocorrência"
resultado: nenhuma ocorrência

A aplicação não utiliza, em nenhum ponto, a facilidade de armazenamento de arquivos do framework — não há gravação em disco local, em volume nem em serviço de armazenamento de objetos.

H.3 — Encaminhamento direto, em memória

O trecho responsável pelo envio dos documentos monta a requisição em memória a partir dos arquivos recebidos e a transmite imediatamente ao prestador contratado. Não há etapa intermediária de gravação:

// SubmitKYCRequest carries the multipart KYC documents. Files are *multipart
// .FileHeader values streamed straight from the inbound HTTP request — nothing
// is buffered to disk by us. For documentType "rg": Selfie, DocumentFront,
// DocumentBack. For "cnh": Selfie, DocumentPhoto.
type SubmitKYCRequest struct {
    DocumentType  string
    Selfie        *multipart.FileHeader
    DocumentFront *multipart.FileHeader
    DocumentBack  *multipart.FileHeader
    DocumentPhoto *multipart.FileHeader
}

Encerrada a requisição HTTP, as estruturas em memória são liberadas pelo coletor de lixo do processo. Nenhum vestígio persiste.

Por que essa evidência importa

As três verificações são de naturezas distintas — catálogo do banco, busca no código e leitura do fluxo — e convergem. Um comprometimento da base de dados do XIP não exporia imagens de documento nem imagens faciais, porque elas não estão lá em nenhuma forma.

Como reproduzir esta verificação

Um parceiro, auditor ou autoridade pode reproduzir integralmente as evidências deste documento. As consultas apresentadas são as mesmas aplicáveis à base de produção; apenas as funções de mascaramento precisam estar disponíveis na sessão.

Evidência Como reproduzir
Esquema das tabelas (A, F) \d receivers e \d webhook_events em sessão de leitura.
Registros e estados (C, D) Executar as consultas apresentadas com as funções de mascaramento aplicadas.
Trilha do prestador (E) Consultar raw_payload do cadastro sob exame.
Bloqueio de operação (G) Executar a consulta de asserção de G.3 e confirmar retorno vazio; inspecionar os trechos de código indicados em G.1 e G.2.
Ausência de imagens (H) Executar a consulta ao catálogo de H.1 e a busca no código de H.2.

Complementarmente, o comportamento do bloqueio é coberto por testes automatizados executados a cada alteração do código, que exercitam o fluxo de verificação e confirmam a recusa de operações para cadastros não habilitados. Os relatórios de execução são disponibilizados mediante solicitação.

Evidência com dados de produção

Esta página é pública e, por essa razão, exibe exclusivamente dados sintéticos. A publicação de registros reais, ainda que mascarados, ampliaria sem necessidade a exposição de dados pessoais de usuários — o que seria incompatível com o princípio da minimização adotado na Política de Proteção de Dados e Privacidade.

Para parceiros em processo de diligência, auditores independentes e autoridades competentes, disponibilizamos, mediante acordo de confidencialidade:

  • as mesmas consultas executadas sobre a base de produção, com mascaramento aplicado, em relatório datado e assinado;
  • demonstração assistida em ambiente controlado, com acompanhamento em tempo real da execução das consultas;
  • relatórios de execução dos testes automatizados que cobrem o fluxo de verificação e o bloqueio transacional;
  • evidências relativas aos arquivos de identificação em custódia do prestador contratado, obtidas junto a ele e por ele atestadas, quanto a existência, integridade, prazo de guarda e controles de acesso;
  • documentação contratual que estabelece as obrigações de conformidade do prestador contratado.

Solicitações devem ser encaminhadas a compliance@xip.cash.

Histórico de versões

Versão Data Alterações
1.0 29/07/2026 Publicação inicial.