Skip to content

Rate Limits de Mensageria — Wasender API

Constante centralizada: supabase/functions/_shared/messaging-rate-limits.ts (backend) e src/lib/messaging-rate-limits.ts (frontend).


Plano Atual: Basic ($6/mês) — Account Protection Mode ATIVO

RecursoLimite
Send Message1 request/5 segundos (12/min) — Account Protection Mode ativo
Send Message (sem APM)256 requests/minuto por sessão
Sessões WhatsApp1
Mensagens/mêsIlimitadas (sem custo por mensagem)
Cap diárioSem 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

PlanoPreço/mêsSend Message/minSessõesCap diário
TrialGrátis256150 msgs
Basic$6256 (12 c/ APM)1Ilimitado
Pro$122563Ilimitado
Plus$182565Ilimitado
Business$3025610Ilimitado

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 });
ConsumidorskipDelayMotivo
send-otptrueUsuário aguardando na tela de login
portal-login-responsaveltrueOTP portal — usuário aguardando
portal-login-alunotrueOTP portal — usuário aguardando
portal-cadastrotrueOTP cadastro — usuário aguardando
escola-pagamentostrueNotificação síncrona ao admin
mercadopago-preferencetrueNotificação síncrona ao gestor
mercadopago-webhooktrueWebhook — single msg
faturamento-cron (faturas)falseBulk background — loop de gestores
faturamento-cron (D-5)falseBulk background — loop de gestores
faturamento-cron (crítico)falseAlerta sistema — loop de admins
maintenance-cron (crítico)falseAlerta 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_otps por usuario_id)
  • Por IP: Máximo 10 OTPs em 1 hora (query em login_otps por ip_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

EscolasDelay/msgTempo totalTimeout EFStatus
3 (staging)6s~18s300s✅ OK
20 (produção)6s~120s300s✅ OK
30 (futuro)6s~180s300s✅ 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 controleFila sequencial (delay embutido no adapter)
Múltiplos workers na mesma sessão1 worker sequencial por sessão
skipDelay: true em bulk backgroundskipDelay apenas para fluxos interativos
Enviar 1000 msgs sem checar sessãoValidar sessão antes, pausar se desconectada

Glossário

TermoSignificado
Account Protection Mode (APM)Configuração Wasender que limita envios a 1 req/5s. Ativo no nosso dashboard.
skipDelayFlag em enviarMensagemComLog() que pula o delay de rate limit para fluxos interativos.
ThrottleLimitar a velocidade de envio (delay fixo entre mensagens). Implementado no adapter.
ConcorrênciaMúltiplos workers enviando em paralelo. Anti-padrão para 1 sessão.
Circuit breakerPadrã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 valores

Histórico

DataMudança
2026-03-30Documento criado. Constante centralizada + cooldown 60s no reenviar OTP.
2026-03-30Correção: concorrência 3→1, delay 250ms→1000ms. Conta anterior estava errada (720/min vs 256 limite).
2026-04-08Account Protection Mode confirmado ativo. Delay 275ms→6000ms. Padrão skipDelay implementado no adapter.