Pular para o conteúdo

Guia de integração: KYT ponta a ponta

Este guia leva a sua engenharia do zero ao ciclo completo da integração de KYT: cadastrar o cliente final, vincular a carteira, analisar a transação, receber o evento e registrar a sua decisão. Todos os passos usam operações do contrato do cliente, autenticadas por X-API-Key.

Pré-requisitos:

  • Conta do tenant liberada para a API pela Sentinex Risk (ver o passo 1 do Quickstart)
  • Chave com os escopos vasp:customers:write, vasp:wallets:write, vasp:transactions:screen, vasp:read e vasp:webhooks:manage
  • URL HTTPS pública para receber webhooks
  • Leitura de Homologação e testes e do Guia de chaves de API

  1. Configure as variáveis de ambiente

    Janela do terminal
    export SENTINEX_API_KEY="snxc_sand_<SUA_CHAVE>" # snxc_live_<SUA_CHAVE> na produção
    export SENTINEX_BASE_URL="https://sandbox.api.crypto.sentinexrisk.com/api/v1" # https://crypto-api.sentinexrisk.com/api/v1 na produção
    export SENTINEX_WEBHOOK_SECRET="$(openssl rand -hex 32)"

    O secret do webhook é gerado por você e enviado no passo 5; a API nunca o devolve. Guarde a chave e o secret num cofre de segredos, nunca no repositório.

  2. Cadastre o cliente final

    A API aceita document (CPF ou CNPJ validado) ou document_hash (SHA-256), um dos dois. Se ambos divergirem, responde 422 DOCUMENT_HASH_MISMATCH. Com document, o servidor guarda índice cego e cifra; somente hash não permite exibir o documento depois. O exemplo usa hash sintético. Os campos livres attributes recusam com 400 chaves e valores que carreguem identificador pessoal em claro. Escopo: vasp:customers:write.

    Janela do terminal
    curl -X POST $SENTINEX_BASE_URL/vasp/customers \
    -H "X-API-Key: $SENTINEX_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuidgen)" \
    -d '{
    "external_id": "CLIENTE-TESTE-001",
    "type": "PF",
    "document_hash": "'$(printf %s "00000000000" | shasum -a 256 | cut -d" " -f1)'",
    "kyc_tier": "STANDARD",
    "risk_classification": "MEDIUM",
    "consent_data_share": true
    }'

    A resposta 201 traz o cliente em data, com id (UUID), kyc_tier, risk_classification e screening_status: "PENDING". O external_id é único por tenant: repeti-lo responde 409 com customer_already_exists.

  3. Vincule uma carteira ao cliente

    O endereço é validado pela rede (checksum EIP-55, bech32, base58 e afins) e gravado só cifrado. Escopo: vasp:wallets:write.

    Janela do terminal
    curl -X POST "$SENTINEX_BASE_URL/vasp/wallets?mode=sync" \
    -H "X-API-Key: $SENTINEX_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuidgen)" \
    -d '{
    "customer_id": "<id devolvido no passo 2>",
    "external_id": "CARTEIRA-TESTE-001",
    "address": "0x2222222222222222222222222222222222222222",
    "blockchain": "eth",
    "asset_symbol": "USDT",
    "wallet_label": "Carteira principal",
    "custody_type": "self_hosted"
    }'

    Com mode=sync (o padrão), a resposta 201 já traz o resultado da triagem da carteira em data: screening_status, decision (APPROVE, REVIEW ou BLOCK) e risk_tier. Com mode=async, a resposta é 202 com screening_status: "PENDING", e o resultado é lido depois em GET /vasp/wallets/{id}. Carteira que o tenant já conhecia responde 200 com o resultado armazenado.

  4. Analise uma transação

    Janela do terminal
    curl -X POST $SENTINEX_BASE_URL/vasp/screenings \
    -H "X-API-Key: $SENTINEX_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuidgen)" \
    -d '{
    "client_transaction_id": "TXN-TESTE-001",
    "customer_ref": "CLIENTE-TESTE-001",
    "direction": "DEPOSIT",
    "asset": "USDT",
    "chain": "ethereum",
    "counterparty_address": "0x1111111111111111111111111111111111111111",
    "amount": 1500.25,
    "fiat_value_brl": 7500.5
    }'

    A resposta é síncrona (escopo vasp:transactions:screen) e traz recommendation (ALLOW, REVIEW, HOLD ou DENY), risk_score, risk_level, sanctions, rule_hits, coverage, obligations e client_decision com status: "PENDING_CLIENT_DECISION". O schema completo está em POST /vasp/screenings.

    customer_ref é uma referência opaca do seu lado: o servidor protege toda referência não vazia com HMAC versionado e isolado por tenant antes de gravar, mesmo quando não reconhece PII. Preserve sua referência original para novas chamadas; não espere eco em claro. Análise sinalizada abre alerta na fila do tenant, que você lê em GET /vasp/alerts.

  5. Receba os eventos

    Cadastre a subscription com o secret do passo 1 e teste o receptor antes do tráfego real:

    Janela do terminal
    curl -X POST $SENTINEX_BASE_URL/vasp/webhooks \
    -H "X-API-Key: $SENTINEX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "target_url": "https://hooks.demovasp.com.br/sentinex",
    "events": ["kyt.screening.completed", "kyt.screening.sanction_hit", "kyt.case.client_decision.recorded"],
    "secret": "'"$SENTINEX_WEBHOOK_SECRET"'"
    }'
    curl -X POST $SENTINEX_BASE_URL/vasp/webhooks/<id devolvido acima>/test \
    -H "X-API-Key: $SENTINEX_API_KEY"

    Nas condições de entrega do tenant live, cada análise concluída chega como kyt.screening.completed, num envelope com event_type, event_id, delivery_id, attempt e o conteúdo do evento em payload. Verifique a assinatura HMAC antes de processar: ver Webhooks.

  6. Registre a sua decisão

    A recomendação da Sentinex Risk não decide nada por você. Registre a ação operacional e o desfecho de suspeição, uma única vez por análise:

    Janela do terminal
    curl -X PATCH $SENTINEX_BASE_URL/vasp/screenings/<screening_id>/client-decision \
    -H "X-API-Key: $SENTINEX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "client_decision": {
    "operational_action": "ALLOW",
    "suspicion_outcome": "NOT_SUSPICIOUS",
    "rationale": "Contraparte conhecida e valor compatível com o perfil do cliente."
    }
    }'

    O registro é imutável: a segunda chamada responde 409 com KYT_CLIENT_DECISION_ALREADY_RECORDED, e o evento kyt.case.client_decision.recorded é enviado às suas subscriptions. O caminho antigo PATCH /vasp/screenings/{id}/decision está obsoleto.

  7. Acompanhe os alertas

    GET /vasp/alerts lista os alertas do tenant, com status opcional, limit de 1 a 200 (padrão 50) e offset. A resposta traz data e o bloco pagination com total, limit e offset. A resolução do alerta é feita pela sua equipe no painel, e cada resolução dispara alert.resolved.

  • Chave live aprovada e emitida, com os escopos mínimos, guardada em cofre de segredos
  • Secret do webhook guardado em cofre, com plano de troca (não há rotação: troca é subscription nova)
  • Idempotency-Key em todo POST /vasp/screenings, POST /vasp/customers e POST /vasp/wallets, com os mesmos bytes a cada retentativa
  • Retentativa com espera crescente para 429 e 5xx, respeitando Retry-After quando vier
  • Validação da assinatura HMAC em toda entrega de webhook
  • X-Request-ID das respostas registrado no seu log, para o suporte cruzar a chamada
  • Acompanhamento de X-RateLimit-Remaining, com alerta antes de chegar a zero
  • Regra de contingência para 503: a análise não foi feita e a transação não foi triada, então não a trate como aprovada

Use Como as regras decidem para tratar ALLOW, REVIEW, HOLD e DENY. Um HTTP 200 com fail_closed: true é análise gravada com retenção, não falha HTTP; reprocessar exige nova chave, pois replay e GET preservam o snapshot antigo. Consulte Dúvidas frequentes para conflitos, medições ausentes e prazos de alerta.