> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unifokal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Proteção de conta

> O módulo conta é um gate síncrono de risco sobre eventos de conta, como login, troca de senha e recuperação de acesso.

## 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`.

> [!WARNING]
>
> **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.

```
// conta no check_details: um login pedindo prova adicional
{ "module": "account_gate", "passed": null, "outcome": "pending", "score": 40,
  "data": {
    "account": {
      "verdict": "step_up",
      "risk_score": 60,
      "reasons": ["new_country", "impossible_travel"],
      "reasons_expired": false,
      "type": "login",
      "step_up_session_id": "vs_..."
    }
  } }
```

**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 detecção mais grave do histórico 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.

> [!WARNING]
>
> **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](https://unifokal.com/precos) 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.