# Validação de canal: e-mail e telefone

<https://unifokal.com/docs/modulos/canal>

## Validação de canal: e-mail e telefone

Três módulos validam o **canal de contato** informado pelo usuário: **Validação de e-mail** (`email_otp`), **Telefone: linha e operadora** (`telefone`) e **Telefone: código por SMS** (`sms_otp`). Eles provam **posse do canal naquele momento** (a pessoa recebe e digita um código, ou o número existe na numeração oficial). **Nenhum deles prova identidade**: quem prova identidade é a biometria e o documento. Use canal como sinal complementar, nunca como autenticador único.

! **Limites honestos.** `telefone` valida o número contra a base oficial de numeração (tipo de linha, DDD, detentora da faixa), mas **não diz se a linha está ativa** nem de quem ela é (portabilidade não é coberta: `range_holder_is_current` vem sempre `false`). `sms_otp` é cobrado **na decisão**, como todo módulo do catálogo, e o critério ali é a **chegada**: o canal entra no preço quando o código é confirmado ou quando o relatório de entrega diz que a mensagem chegou. Mensagem que saiu sem confirmação de entrega e sem código digitado fica fora. Como quem cobra é a decisão, sessão abandonada antes dela não gera cobrança nenhuma, mesmo que o SMS já tenha saído. E confirmar um e-mail prova acesso à caixa de entrada, **não a identidade** de quem digitou.

`email_otp` e `sms_otp` são **interativos**, e o contato dos dois vem do mesmo lugar: **o titular digita no widget**, e-mail e telefone igualmente. Você nunca envia contato na criação da sessão, e não há nada a chamar do seu servidor: o próprio widget envia o código (6 dígitos, validade de 10 minutos, reenvio com intervalo mínimo) e o confirma. O módulo `telefone` também não pede nada: ele roda sozinho no pipeline, sem enviar mensagem.

**Os limites do código**, para você desenhar a sua tela e instruir o seu suporte: ele vale por **10 minutos**, aceita **3 tentativas** erradas e trava na terceira, e o reenvio só libera **60 segundos** depois do envio anterior. Cada sessão comporta no máximo **3 envios no e-mail** e **2 no SMS**, contando o primeiro.

Os dois fins de linha são **diferentes**, e o seu suporte precisa separá-los. Esgotar as **tentativas** encerra aquele código, e o titular pede outro no próprio widget, o que funciona enquanto sobrar envio. Esgotar os **envios** é o fim da linha da sessão: não existe código novo ali, insistir não resolve, e o que resta é uma sessão nova.

No webhook, cada módulo de canal entra em `check_details` com os seus metadados. **Nunca** trafegam o código nem o contato completo: só o mascarado, o domínio e a impressão digital (`fp`, um HMAC do destino, estável para correlacionar sem expor).

```
// email_otp
{ "module": "email_otp", "passed": true, "outcome": "approved", "score": 100,
  "data": { "verified": true, "email_domain": "gmail.com", "email_masked": "j***o@gmail.com", "email_fp": "b3f1c9…", "destination_source": "end_user", "attempts": 1, "sends": 1, "verified_at": "2026-06-17T14:31:40Z" } }

// sms_otp
{ "module": "sms_otp", "passed": true, "outcome": "approved", "score": 100,
  "data": { "verified": true, "phone_masked": "+55 11 9****-**99", "phone_fp": "9f2c1a…", "country_code": "55", "destination_source": "end_user", "attempts": 1, "sends": 1, "verified_at": "2026-06-17T14:31:52Z" } }
```

Os dois são **portões suaves** na decisão: canal não confirmado deixa a verificação em `review`, nunca reprova sozinho. Em sandbox nada é enviado: o código fixo `000000` confirma, e destino terminado em `9` simula um destino recusado, sem cobrança.
