Rate Limits de Mensageria — Wasender API
Constante centralizada:
supabase/functions/_shared/messaging-rate-limits.ts(backend) esrc/lib/messaging-rate-limits.ts(frontend).
Plano Atual: Basic ($6/mês) — Account Protection Mode ATIVO
| Recurso | Limite |
|---|---|
| Send Message | 1 request/5 segundos (12/min) — Account Protection Mode ativo |
| Send Message (sem APM) | 256 requests/minuto por sessão |
| Sessões WhatsApp | 1 |
| Mensagens/mês | Ilimitadas (sem custo por mensagem) |
| Cap diário | Sem limite (apenas Trial tem 50/dia) |
⚠️ Account Protection Mode está ativo no dashboard Wasender. Isso sobrescreve o limite de 256/min para 1 req/5s (12 req/min). O delay de 6s entre envios garante margem de 20%.
Tabela Comparativa de Planos
| Plano | Preço/mês | Send Message/min | Sessões | Cap diário |
|---|---|---|---|---|
| Trial | Grátis | 256 | 1 | 50 msgs |
| Basic | $6 | 256 (12 c/ APM) | 1 | Ilimitado |
| Pro | $12 | 256 | 3 | Ilimitado |
| Plus | $18 | 256 | 5 | Ilimitado |
| Business | $30 | 256 | 10 | Ilimitado |
Nota: Com Account Protection Mode ativo, o limite efetivo é 12 req/min independente do plano.
Proteções Implementadas
1. Delay Centralizado no Adapter (Rate Limit Wasender)
- Valor: 6000ms (6 segundos) entre envios — margem 20% sobre limite de 5s
- Onde:
enviarMensagemComLog()em_shared/wasender-whatsapp.ts - Constante:
MESSAGING_RATE_LIMITS.delayEntreEnviosMs - Comportamento: Após envio bem-sucedido via Wasender, aguarda 6s antes de retornar
- skipDelay: Consumidores interativos passam
{ skipDelay: true }para não travar o usuário
Padrão skipDelay
typescript
// Bulk/background — pega delay automaticamente (padrão)
await enviarSMSComLog({ to, message, ... });
// Interativo — skip delay para não travar UX
await enviarSMSComLog({ to, message, ... }, { skipDelay: true });| Consumidor | skipDelay | Motivo |
|---|---|---|
| send-otp | true | Usuário aguardando na tela de login |
| portal-login-responsavel | true | OTP portal — usuário aguardando |
| portal-login-aluno | true | OTP portal — usuário aguardando |
| portal-cadastro | true | OTP cadastro — usuário aguardando |
| escola-pagamentos | true | Notificação síncrona ao admin |
| mercadopago-preference | true | Notificação síncrona ao gestor |
| mercadopago-webhook | true | Webhook — single msg |
| faturamento-cron (faturas) | false | Bulk background — loop de gestores |
| faturamento-cron (D-5) | false | Bulk background — loop de gestores |
| faturamento-cron (crítico) | false | Alerta sistema — loop de admins |
| maintenance-cron (crítico) | false | Alerta sistema — loop de admins |
2. Cooldown no Frontend (Reenviar OTP)
- Valor: 60 segundos entre reenvios
- Onde:
src/components/login-unified.tsx— botão "Reenviar código" - Constante:
MESSAGING_RATE_LIMITS.cooldownReenvioOtpSegundos - Comportamento: Após envio de OTP, botão mostra countdown "Reenviar em Xs" e fica desabilitado
- Natureza: Apenas UX — a proteção real é no backend
3. Rate Limit no Backend (OTP) — PROTEÇÃO REAL
- Por usuário: Máximo 3 OTPs em 15 minutos (query em
login_otpsporusuario_id) - Por IP: Máximo 10 OTPs em 1 hora (query em
login_otpsporip_origem) - Onde: Edge Function
send-otp - Resposta: HTTP 429 quando excedido
- Proteção contra: CURL, bots, requisições em massa — qualquer client
4. Adapter Centralizado
- Todo envio passa por
enviarMensagemComLog()em_shared/wasender-whatsapp.ts - Ponto único de envio via WhatsApp (Wasender)
- Ponto único para delay de rate limit
Timeout de Edge Functions
| Escolas | Delay/msg | Tempo total | Timeout EF | Status |
|---|---|---|---|---|
| 3 (staging) | 6s | ~18s | 300s | ✅ OK |
| 20 (produção) | 6s | ~120s | 300s | ✅ OK |
| 30 (futuro) | 6s | ~180s | 300s | ✅ OK |
| 45+ | 6s | ~270s+ | 300s | ⚠️ Perto do limite |
Nota: Se ultrapassar 40 escolas, considerar dividir o processamento em batches ou desativar Account Protection Mode no Wasender.
Guia para Envio em Massa (Futuro)
Conta correta (com Account Protection Mode)
text
Limite real: 1 request/5 segundos (Account Protection Mode ativo)
Configuração segura:
1 worker sequencial × delay de 6000ms = 1 msg/6s = 10 msgs/minuto
10/12 = 83% do limite → margem de 17%Implementação
typescript
import { MESSAGING_RATE_LIMITS } from "../_shared/messaging-rate-limits.ts";
// Fila sequencial — NUNCA paralela
// O delay já está embutido no enviarMensagemComLog() via adapter
for (const destinatario of lista) {
await enviarMensagemComLog(destinatario, mensagem);
// Delay automático de 6s após cada envio bem-sucedido
}Por que NÃO usar concorrência
A documentação do Wasender afirma que alta concorrência é a causa #1 de ban do WhatsApp.
Com 1 sessão (plano Basic), múltiplos workers disparam requests simultâneos contra o mesmo limite:
text
❌ 3 workers × qualquer delay = múltiplos requests simultâneos (BAN CERTO)
❌ Promise.all sem controle = rajada instantânea (BAN CERTO)
✅ 1 worker × 6000ms delay = 10 msgs/min (seguro, margem 17%)Anti-padrões
| ❌ Errado | ✅ Correto |
|---|---|
Promise.all(msgs.map(enviar)) — rajada sem controle | Fila sequencial (delay embutido no adapter) |
| Múltiplos workers na mesma sessão | 1 worker sequencial por sessão |
skipDelay: true em bulk background | skipDelay apenas para fluxos interativos |
| Enviar 1000 msgs sem checar sessão | Validar sessão antes, pausar se desconectada |
Glossário
| Termo | Significado |
|---|---|
| Account Protection Mode (APM) | Configuração Wasender que limita envios a 1 req/5s. Ativo no nosso dashboard. |
| skipDelay | Flag em enviarMensagemComLog() que pula o delay de rate limit para fluxos interativos. |
| Throttle | Limitar a velocidade de envio (delay fixo entre mensagens). Implementado no adapter. |
| Concorrência | Múltiplos workers enviando em paralelo. Anti-padrão para 1 sessão. |
| Circuit breaker | Padrão que pausa envios após N falhas consecutivas para evitar cascata de erros. |
Checklist: Mudança de Plano
markdown
□ Atualizar `MESSAGING_RATE_LIMITS.plano` e `custoMensal`
□ Atualizar `sessoesDisponiveis` se mudou
□ NÃO aumentar `maxConcorrencia` além de 1 por sessão
□ Verificar se `accountProtectionMode` está habilitado no dashboard Wasender
□ Se APM desativado: reduzir `delayEntreEnviosMs` para 275ms
□ Atualizar tabela comparativa neste documento
□ Testar bulk sending com novos valoresHistórico
| Data | Mudança |
|---|---|
| 2026-03-30 | Documento criado. Constante centralizada + cooldown 60s no reenviar OTP. |
| 2026-03-30 | Correção: concorrência 3→1, delay 250ms→1000ms. Conta anterior estava errada (720/min vs 256 limite). |
| 2026-04-08 | Account Protection Mode confirmado ativo. Delay 275ms→6000ms. Padrão skipDelay implementado no adapter. |