ZAP-API
PreçosCasos de UsoBlogDocsLogin
Começar grátis
  1. Blog
  2. Circuit breaker em WhatsApp API: proteja sua instância de sobrecarga
Arquitetura

Circuit breaker em WhatsApp API: proteja sua instância de sobrecarga

Implemente circuit breaker para sua integração WhatsApp: evite cascata de falhas, proteja sua instância de rate limiting e garanta degradação graciosa.

15 de maio de 2026·13 min de leitura·Equipe Editorial ZAP API

Imagine: você tem uma campanha de Black Friday rodando e dispara 10.000 mensagens em 2 minutos para uma única instância WhatsApp. O que acontece? Spoiler: nada bom. Sua instância recebe rate limiting do WhatsApp, começa a falhar 30% das requisições, sua aplicação fica ocupada fazendo retry de mensagens que vão falhar de novo, o gateway sobrecarrega, e em alguns minutos você está com a instância em modo de "supervisão" — uma forma educada de dizer "quase banida".

O padrão circuit breaker existe exatamente para esse cenário. Ele detecta que o sistema downstream está degradado e para de mandar requisições até ele se recuperar. Em vez de empurrar trabalho para um sistema doente, você dá tempo dele respirar.

O que acontece sem proteção

Cenário real de um e-commerce que mandou disparo em massa em 2024:

  • Minuto 0-2: 10k mensagens enfileiradas, taxa de envio 100/seg, tudo ok
  • Minuto 2-5: WhatsApp começa a retornar 429 (rate limit) em ~30% das mensagens. App faz retry imediato. Volume real triplica para 300/seg.
  • Minuto 5-8: Gateway saturado, latência de 2s vai para 30s, timeouts em massa. Aplicação faz mais retry achando que foi erro de rede.
  • Minuto 8-15: Instância marcada como suspeita pelo WhatsApp. Mensagens param de chegar nos clientes (sumiram em algum lugar do meio do caminho). Usuário liga reclamando.
  • Resultado: 4.000 mensagens entregues, 6.000 perdidas, 3 horas para o WhatsApp normalizar a sessão. Campanha foi para o lixo.

Com circuit breaker no lugar, no minuto 3 o sistema teria parado de mandar por 30 segundos, deixado a fila acumular, e voltado quando o gateway estivesse saudável. Resultado seria: campanha 30 min mais lenta, mas 9.700 das 10.000 mensagens entregues.

Os 3 estados

CLOSED (operação normal)

Tudo passa. Cada falha é contada. Se a contagem ultrapassar o threshold (ex.: 5 falhas em 1 minuto), muda para OPEN.

OPEN (curto-circuito ativo)

Toda requisição é imediatamente rejeitada, sem chegar no gateway. O cliente da função recebe um erro CircuitBreakerOpenError e decide o que fazer (enfileirar, alertar, descartar). Após um timeout (ex.: 30s), muda para HALF_OPEN.

HALF_OPEN (teste cuidadoso)

Permite passar até N requisições (ex.: 3) para testar se o gateway voltou. Se as N passarem, volta para CLOSED. Se qualquer falhar, volta para OPEN com timeout dobrado (backoff exponencial).

Implementação completa em Node.js

// circuit-breaker.ts
type CircuitState = "CLOSED" | "OPEN" | "HALF_OPEN";

interface CircuitOptions {
  failureThreshold: number;       // ex.: 5 falhas
  windowMs: number;                // janela de contagem (ex.: 60_000)
  openTimeoutMs: number;           // tempo no OPEN (ex.: 30_000)
  halfOpenMaxAttempts: number;     // tentativas em HALF_OPEN (ex.: 3)
  name: string;                    // identificação para logs/métricas
}

export class CircuitBreaker {
  private state: CircuitState = "CLOSED";
  private failures: number[] = [];        // timestamps de falhas recentes
  private openedAt = 0;
  private halfOpenAttempts = 0;
  private halfOpenSuccesses = 0;
  private currentTimeoutMs: number;

  constructor(private opts: CircuitOptions) {
    this.currentTimeoutMs = opts.openTimeoutMs;
  }

  async execute(fn: () => Promise): Promise {
    this.transitionIfNeeded();

    if (this.state === "OPEN") {
      throw new Error(`CircuitBreaker[${this.opts.name}] is OPEN`);
    }

    if (this.state === "HALF_OPEN") {
      if (this.halfOpenAttempts >= this.opts.halfOpenMaxAttempts) {
        throw new Error(`CircuitBreaker[${this.opts.name}] HALF_OPEN limit reached`);
      }
      this.halfOpenAttempts++;
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (err) {
      this.onFailure();
      throw err;
    }
  }

  private transitionIfNeeded() {
    const now = Date.now();
    if (this.state === "OPEN" && now - this.openedAt >= this.currentTimeoutMs) {
      this.state = "HALF_OPEN";
      this.halfOpenAttempts = 0;
      this.halfOpenSuccesses = 0;
    }
    // Limpa falhas fora da janela
    this.failures = this.failures.filter(t => now - t < this.opts.windowMs);
  }

  private onSuccess() {
    if (this.state === "HALF_OPEN") {
      this.halfOpenSuccesses++;
      if (this.halfOpenSuccesses >= this.opts.halfOpenMaxAttempts) {
        this.state = "CLOSED";
        this.failures = [];
        this.currentTimeoutMs = this.opts.openTimeoutMs; // reset backoff
      }
    } else if (this.state === "CLOSED") {
      // ok, nada a fazer
    }
  }

  private onFailure() {
    if (this.state === "HALF_OPEN") {
      this.state = "OPEN";
      this.openedAt = Date.now();
      this.currentTimeoutMs = Math.min(this.currentTimeoutMs * 2, 5 * 60_000); // dobra até 5min
      return;
    }
    this.failures.push(Date.now());
    if (this.failures.length >= this.opts.failureThreshold) {
      this.state = "OPEN";
      this.openedAt = Date.now();
    }
  }

  getState() { return this.state; }
}

Integrando com BullMQ

Sua fila de mensagens deve respeitar o breaker. Se ele estiver aberto, o job volta para a fila (não é descartado).

import { Worker } from "bullmq";
import axios from "axios";
import { CircuitBreaker } from "./circuit-breaker";

const ZAP = axios.create({
  baseURL: "https://api.zap-api.tech/v1",
  headers: { Authorization: `Bearer ${process.env.ZAP_TOKEN}` },
});

// Um breaker por instância — uma instância em apuros não derruba as outras
const breakers = new Map();

function breakerFor(instanceId: string) {
  if (!breakers.has(instanceId)) {
    breakers.set(instanceId, new CircuitBreaker({
      name: instanceId,
      failureThreshold: 5,
      windowMs: 60_000,
      openTimeoutMs: 30_000,
      halfOpenMaxAttempts: 3,
    }));
  }
  return breakers.get(instanceId)!;
}

new Worker("messages", async (job) => {
  const { instanceId, phone, text } = job.data;
  const breaker = breakerFor(instanceId);

  await breaker.execute(async () => {
    await ZAP.post(`/instances/${instanceId}/messages`, {
      to: phone, type: "text", text,
    });
  });
}, {
  // Configurar BullMQ para fazer retry com backoff
  // Quando o breaker abrir, jobs falham e BullMQ reagenda
});

Webhook de throttle

A ZAP API expõe webhook quando detecta que sua instância está perto de rate-limit. Use isso como sinal externo para abrir o breaker preventivamente.

app.post("/webhook/zap", async (req, res) => {
  res.status(200).send("ok");
  if (req.body.type === "instance.throttle") {
    const breaker = breakerFor(req.body.instanceId);
    // força abertura por 60s
    await breaker.execute(async () => { throw new Error("preemptive"); }).catch(() => {});
    // (na implementação real, expor método breaker.forceOpen())
  }
});

Métricas que importam

Sem métricas, o breaker é uma caixa preta. Exponha:

  • Estado atual por instância (CLOSED/OPEN/HALF_OPEN)
  • Total de aberturas nas últimas 24h
  • Taxa de erro upstream (% requisições que falham antes do breaker)
  • Latência p95/p99 das requisições que passam
  • Tempo médio em OPEN — se for muito alto, gateway downstream tem problema sério

Em Prometheus/Grafana, alerte quando o estado fica OPEN por mais de 5 minutos seguidos: significa que o problema downstream não é transitório, e você precisa intervir manualmente.

Casos práticos

Caso 1: SaaS de cobrança

Plataforma de cobrança PIX que dispara lembretes para 50.000 inadimplentes/dia. Antes do breaker, dia primeiro do mês era sempre caos: gateway congestionado, 20% das mensagens perdidas. Com breaker por instância (5 instâncias rodando), a campanha leva 4h em vez de 1h, mas entrega 99,1% das mensagens.

Caso 2: E-commerce na Black Friday

Loja virtual com pico de 8.000 confirmações de pedido em 30 minutos. Breaker abre 12 vezes ao longo da campanha, cada vez por ~20 segundos. Cliente nem percebe — BullMQ reagenda. Sem o breaker no ano anterior, foi rate-limit total e instância suspensa por 4h, prejuízo estimado de R$80k em pedidos não confirmados.

Caso 3: Bot de atendimento de saúde

Bot de uma operadora de saúde recebe pico de 200 mensagens/min após mandar comunicado em massa. Breaker protegeu o sistema de OCR/IA por trás (cada mensagem chama OpenAI) — quando OpenAI ficou lenta, breaker abriu, sistema voltou a responder com mensagem de "estamos com volume alto, retornaremos em instantes" e enfileirou para depois.

FAQ

Qual a diferença entre circuit breaker e retry?

Retry tenta de novo a mesma requisição. Circuit breaker decide se vale a pena tentar. Eles trabalham juntos: o retry só faz sentido se o breaker estiver fechado. Tentar 5x quando você sabe que o sistema está fora é desperdício e piora o problema.

Devo ter um breaker global ou um por instância?

Por instância. Uma instância com problema não deve afetar as outras. Custos: pequena memória extra (alguns kB por breaker). Benefícios: isolamento real e diagnóstico granular ("qual instância está doente?").

Falso positivo é problema?

Sim, e a defesa principal é o threshold. Se você abrir o breaker com 1 falha, qualquer hiccup de rede para tudo. Use threshold de 5+ falhas em janela de 60s e timeout inicial de 30s. Cresça o threshold se ainda assim sentir abertura desnecessária.

Como observar o breaker em produção?

Exporte métricas Prometheus: circuit_state{name="..."} (0=closed, 1=halfopen, 2=open), circuit_failures_total, circuit_opens_total. Crie alerta no Grafana: estado=OPEN por >5min envia notificação no Slack.

Como recupero da abertura automaticamente?

O HALF_OPEN cuida disso. Após o timeout, deixa passar N requisições de teste — se passarem, fecha. Se falharem, dobra o timeout (exponencial) até no máximo 5min. Depois de problema persistente por horas, alguém precisa investigar manualmente — não tente recuperar de tudo automaticamente.

Próximo passo

Implemente o breaker acima em uma tarde e proteja sua operação do próximo pico inesperado. Criar conta grátis e comece a integrar.

Experimente a ZAP API gratuitamente

7 dias de trial sem precisar de cartão. A partir de R$29/mês*.

Criar instância grátis
EE
Equipe Editorial ZAP APIRevisão técnica

Desenvolvedores e especialistas em integrações WhatsApp. Todo conteúdo passa por revisão técnica para garantir precisão e aplicabilidade.

Ver perfil completoDocumentaçãoTrial grátis

Leia também

Arquitetura · 26 de mai. de 2026 · 14 min

Multi-instância WhatsApp: arquitetura para escalar sem banimento

Como distribuir envios entre múltiplas instâncias WhatsApp para escalar volume, isolar clientes e proteger números críticos de banimento por sobrecarga.

Arquitetura · 10 de mai. de 2026 · 13 min

Cobrança PIX via WhatsApp: integração completa com QR Code

Como integrar cobrança PIX ao WhatsApp: gerar QR Code, enviar pelo chat, receber confirmação de pagamento via webhook e atualizar o status automaticamente.

Arquitetura · 30 de abr. de 2026 · 14 min

Como escalar WhatsApp para 100 mil mensagens/dia: arquitetura, fila e custo real

Limites por instância, cálculo de quantas você precisa, outbox pattern, circuit breaker, warmup pool e 4 métricas vitais. Custo unitário de R$ 0,0009/msg.

Arquitetura · 30 de mai. de 2026 · 13 min

Mensagem idempotente no WhatsApp: nunca envie a mesma mensagem duas vezes

Como implementar idempotência em disparos WhatsApp para evitar mensagens duplicadas em retries, falhas de rede e race conditions em sistemas distribuídos.

Tópicos:Chatbots com IAE-commerceAPI WhatsApp

Explore também

Casos de usoWhatsApp API por segmentoComparativoZAP API vs alternativasPreçosPlanos e o que está inclusoGlossárioTermos técnicos de WhatsApp API
ZAP-API

API REST para WhatsApp com webhooks assinados, Meta Pixel/CAPI e compliance LGPD. Sem aprovação da Meta.

Status operacional🇧🇷 Feito no Brasil

Produto

  • Preços
  • Casos de uso
  • Comparativo
  • Trial grátis
  • Dashboard

Recursos

  • Documentação
  • Blog
  • Glossário
  • RSS Feed

Empresa

  • Sobre
  • Imprensa
  • Termos de uso
  • Privacidade
  • Criar conta
  • Login

Contato

  • [email protected]
  • [email protected]
  • Resposta em até 24h úteis
© 2026 ZAP-API — Todos os direitos reservados·CNPJ 42.130.949/0001-56·Termos·Privacidade

Desenvolvido por PreviusIA