Pular para o conteúdo principal

Proteção de conta

O módulo account_gate é um gate síncrono de risco sobre um evento de conta: seu backend nos manda o evento e recebe na hora um veredito explicável, para decidir se pede uma prova a mais antes de deixar a ação seguir. São sete tipos de evento: login, login falhado, cadastro, troca de senha, recuperação de acesso, troca de e-mail e ação sensível. O flow com account_gate tem só ele. O evento chega no bloco account_event da criação da sessão e é decidido ali, sem passo de captura, então POST /v1/flows e PATCH /v1/flows/{id} recusam a composição mista com 422 account_gate_module_exclusive. Mantenha o seu KYC no flow que você já tem e crie um flow separado só com account_gate; a prova a mais mora no flow que step_up_flow_id aponta. O link hospedado não leva o evento e recusa esse flow com 422 account_event_required.
Este não é o módulo da seção acima, nem o da seção antes dela. O transaction_gate avalia um pagamento parado esperando liberação. O transaction_monitor varre a janela de transações depois, sem ninguém esperando. O account_gate avalia um acesso, no instante em que ele acontece. E ele também não é o session_monitor: aquele observa a sessão já logada ao longo do tempo; este responde a um evento pontual e termina ali. São quatro produtos diferentes, com preços diferentes e payloads diferentes.
Os sinais da v1 são publicados pelo nome porque um gate que não diz o que olha é uma caixa preta: impossible_travel (dois acessos separados por uma distância que não dá para vencer no tempo entre eles), failed_login_burst (rajada de logins falhados), new_country (país que nunca apareceu para aquele titular), recent_credential_change (a credencial mudou há pouco), hosting_asn (o IP é de rede de hospedagem, não de acesso residencial ou móvel), ip_risk (o risco do próprio IP), no_verified_identity (o titular nunca fez onboarding com você aqui, ou fez há mais de 180 dias e não teve atividade de conta ou de transação com você desde então), dormant_reactivation (conta parada há muito tempo que volta a se mexer), new_isp (a operadora de rede nunca apareceu para aquele titular), odd_hour (horário sem nenhum acesso anterior no histórico dele), ip_shared (o mesmo IP falhando contra várias contas suas na mesma janela) e disposable_email (você nos declarou que o e-mail da conta é de domínio descartável). disposable_email é o único que depende de você: mande account_event.disposable_email como true ou false quando souber. Sem o campo, o sinal não vira “e-mail limpo”: ele volta em reasons como disposable_email_unknown, que é como declaramos todo sinal que não foi possível medir. A comparação é com o histórico do próprio titular na sua base: países, ASNs, horários e a velocidade entre eventos consecutivos, tudo isolado por cliente. Não há consórcio: o histórico de outro cliente nunca entra na conta. Não compramos reputação de IP de terceiro: o país e o ASN saem da nossa base GeoIP local, e o resto sai do que você mesmo nos mandou.
Repare que o data é aninhado sob account. É a mesma razão do session_monitor e do transaction_monitor: o evento chega no mesmo verification.completed do onboarding, e um integrador que guarda “a última verificação por reference_id” sobrescreveria o KYC daquele titular com um veredito de login. Com a chave própria, o KYC e o acesso nunca disputam o mesmo lugar no seu banco. São três vereditos, e nenhum deles bloqueia. allow é “nada anormal”. step_up é “peça mais uma prova antes de deixar entrar”, nunca um não, e ele jamais vira approved: com step-up configurado no flow, step_up_session_id vem junto e é a sessão de prova aberta para o titular. deny é o corte mais alto, e mesmo ele sai do nosso motor como revisão. O pior desfecho que emitimos é revisar, e quem aplica qualquer consequência sobre a conta é você. O nível do gate. Na tela Motor do painel, você escolhe o nível deste gate: Conservador, Equilibrado ou Conversão, do mais rigoroso ao que mais converte. O Conservador é o padrão. Veja o efeito no seu histórico antes de ligar, e observe em sombra sem mudar nenhuma decisão. reasons fica disponível por 180 dias contados da decisão. Depois disso, a consulta pelo painel, a reemissão e a exportação trazem o veredito, o risco, o type e o step_up_session_id, com reasons vazia e reasons_expired: true (antes do prazo ele vem false, sempre presente). O corpo guardado de cada entrega sai junto, no mesmo prazo: depois dele, o replay e o reenvio pelo painel respondem payload_unavailable. Lista vazia com reasons_expired: true quer dizer que as razões saíram no prazo, nunca que nada acendeu. No mesmo prazo a referência sai da verificação do evento: a consulta e a exportação trazem reference_id: null, e a busca pela referência deixa de achar essa verificação; o verification_id não muda. Guarde no recebimento do webhook o que precisar guardar por mais tempo, inclusive para a avaliação anual de efetividade, se o seu regime a pede. Cada evento leva o seu external_id. account_event.external_id é obrigatório: é o identificador único do evento no seu sistema, de 1 a 255 caracteres (letras, dígitos e . _ : @ -, sem dado pessoal). Ele é a chave de idempotência do evento: repetir o mesmo external_id em até 600 segundos devolve a mesma resposta, e outro evento do mesmo titular é sempre avaliado. Step-up com prova de humano. Quando o flow do gate tem um flow de prova configurado (step_up_flow_id, com um flow de reserva opcional em step_up_fallback_flow_id) e o veredito é step_up, o bloco decision.step_up da resposta já traz a prova. Com available: true, session_id é a sessão de PROVA, válida por 600 segundos: monte o widget com ela e o titular confirma o acesso com a passkey da conta dele ou com a reautenticação facial, conforme o fator que a conta tem (factor). O resultado chega no webhook da sessão de prova, com data.step_up_of: source é account_gate, verification_id é a verificação do evento e event_type é o tipo do evento. A verificação do evento não muda por causa da prova. Com available: false, reason diz por quê (por exemplo no_factor_enrolled) e você aplica o seu próprio desafio. Se o motivo for gate_budget_exceeded, o orçamento que você configurou para a chave acabou e o evento não foi avaliado: falhe fechado e aplique o seu desafio. O teto diário do módulo conta só signup e recovery: acima dele, o evento continua avaliado e não é cobrado, mas a prova não abre, e reason sai step_up_rate_limited: aplique o seu desafio. Os demais tipos não entram em teto da casa e são sempre cobrados (menos login_failed); para limitá-los, configure o orçamento da chave. Mande login só depois de a credencial conferir: a tentativa que falhou vai como login_failed. Um login_failed nunca abre prova. Repetir o mesmo external_id depois dos 600 segundos devolve o mesmo pedido enquanto ele estiver aberto, e step_up_closed depois. Quando a prova é aprovada com a passkey, o histórico do titular aprende o país e a rede daquele evento, para o mesmo acesso legítimo não pedir prova de novo; aprovada só pelo rosto, ou não concluída, nada é aprendido. Para bônus e saque há dois tipos próprios, os dois sensíveis: reward_claim (resgate de bônus, indicação ou cupom) e withdrawal (saque ou retirada de saldo). Neles o gate confere a última detecção de múltiplas contas (unique_face) do mesmo reference_id: rosto duplicado entra em reasons como subject_face_duplicate, e titular sem essa detecção no histórico sai com face_uniqueness_unknown, nunca como rosto único.
O campo device.fingerprint é opcional. Ele é aceito no contrato de entrada e está descrito na referência da API, e o módulo decide com ou sem ele: a avaliação do evento de conta não depende desse campo para sair completa. É a mesma trava jurídica do módulo Sinais do aparelho, que continua pausado enquanto o pacote do legítimo interesse não fechar (o teste de balanceamento do art. 7º IX, a transparência e o mecanismo de oposição). Qualquer mudança no que o campo passa a valer é anunciada no changelog, nunca silenciosa.
O que este módulo não faz. Ele não bloqueia ninguém, como acabou de ser dito. Ele não autentica e não emite segundo fator: o step_up é uma recomendação, e quem pede a prova, escolhe qual é e opera o próprio login continua sendo você. Ele não consulta bureau nem compra reputação de IP. E ele não observa a sessão depois: terminado o evento, o módulo não tem mais nada a dizer sobre aquele titular até o próximo evento chegar. A unidade cobrada é o evento de conta avaliado, e não a verificação nem o titular por mês. Um mesmo titular que faz vinte logins no mês gera vinte eventos avaliados, e a tabela de preços imprime a unidade ao lado do valor para a conta fechar antes da integração. Tentativa de login com falha não é cobrada. O evento login_failed é avaliado, entra no histórico do titular e alimenta a rajada de logins falhados, mas sai do preço: ele é o rastro do ataque que a sua base sofre, e cobrar por ele faria o atacante gastar o seu saldo. A verificação dele sai com billable: false. E um 402 por saldo insuficiente num fluxo com este módulo nunca aciona a recarga automática do seu cartão.