· campaign reliability & auto-responder triage · Founder, Klaros

The WhatsApp broadcast that started talking to itself: Eliminating auto-responder loops

Out-of-office auto-responders and BSP system error messages trigger infinite bot ping-pong loops on WhatsApp campaigns. Meta Cloud API provides no is_bot header, so standard CRMs either loop indefinitely or mute real customers. Here is how we engineered deterministic correspondent triage, context-aware thresholding, and prompt refusal gates in Klaros.

TL;DR / Executive Summary

The Incident: A mass promotional broadcast triggers out-of-office responses from recipient handsets. Naive CRMs (like WATI, AiSensy, or Interakt) treat machine text as human engagement, generating automated AI replies that trigger another machine response—locking both systems into a loop that degrades WABA Quality Ratings and drains messaging credits.

The Architectural Fix: We added dynamic thresholding (15s gap, 60-character threshold for broadcast responses), multilingual auto-responder pattern matching (isAutoResponderPattern), and prompt guard refusal rails (canAnswer: false). Quick-reply button taps and WhatsApp Flow submissions retain 100% unconditional priority.

Legacy CRMs (Naïve Loop) 1. Broadcast Outreach Send 2. Handset OOO Auto-Reply 3. Un-gated AI Auto-Reply Infinite Ping-Pong Loop Klaros (Refusal Engine) 1. Broadcast Outreach Send 2. Handset OOO Auto-Reply 100% Suppressed Gate WABA Quality Protected
Figure 1: Legacy un-gated auto-replies vs Klaros deterministic refusal gate on broadcast out-of-office responses.

The Absence of `Auto-Submitted` Headers on WhatsApp

In conventional messaging protocols, machine-generated traffic carries explicit structural headers:

The Meta WhatsApp Cloud API provides no such header. An inbound webhook payload delivers identical fields (from, wa_id, text) whether written by a human typing on a keyboard or generated by an Android out-of-office auto-reply app:

Inbound Meta Cloud API Payload (Auto-Responder vs Human)
{
  "object": "whatsapp_business_account",
  "entry": [{
    "changes": [{
      "value": {
        "messaging_product": "whatsapp",
        "contacts": [{ "profile": { "name": "Customer" }, "wa_id": "34600000001" }],
        "messages": [{
          "from": "34600000001",
          "id": "wamid.HBgL...",
          "timestamp": "1717584000",
          "text": { "body": "Thank you for reaching out! We are currently unavailable." },
          "type": "text"
        }]
      }
    }]
  }]
}
src/services/correspondent.mjs — Multilingual Auto-Responder Matcher
const AUTOREPLY_PATTERNS = [
  /unknown\s+input\s+type\s+received/i,
  /kindly\s+reply\s+with/i,
  /thank\s+you\s+for\s+(reaching\s+out|contacting|messaging)/i,
  /gracias\s+por\s+(escribirnos|contactarnos|tu\s+mensaje)/i,
  /shukriya|dhanyawad/i,
  /we\s*['’]?\s*re\s+unavailable/i,
  /out\s+of\s+office/i,
  /fuera\s+de\s+oficina/i,
  /auto-reply|auto\s+reply|automated\s-response/i,
];

export function isAutoResponderPattern(text) {
  const norm = String(text || '').trim();
  if (!norm) return false;
  return AUTOREPLY_PATTERNS.some((pattern) => pattern.test(norm));
}
Dynamic Threshold Classification Boundary Response Timing Gap (seconds) Length (chars) Organic Gap (3s, 200c) Broadcast Adaptive Zone (15s, 60c)
Figure 2: Dynamic threshold expansion for broadcast context vs organic 1-on-1 conversations.

Broadcast Attribution & Interactive Immunity

A major trap in WhatsApp CRM architecture is treating standard 1-on-1 operational templates (OTPs, appointment reminders) as mass broadcasts. Klaros inspects origin_campaign_id and is_broadcast flags on the outbound record before applying campaign-level threshold rules.

Crucially, interactive button taps and WhatsApp Flow completions take unconditional priority (isInteractive = true). No matter how fast an interactive payload arrives, a button press is treated as HUMAN 100% of the time.

3-Tier Classification State Machine & Immunity Gate Inbound Message Interactive? HUMAN (Immunity) Classify Rules AUTOMATED (Suppressed)
Figure 3: 3-tier classification state machine and interactive button tap immunity gate.

Generative LLM Prompt Refusal Guards

Even if an automated text bypasses regex matching, generative AI models must be restrained from entering conversational loops. Klaros injects an explicit AUTO-RESPONDER GUARD into the model context when broadcast metadata is present:

src/services/inbound/ai-auto-reply.mjs — Prompt Refusal Guard
const AUTO_RESPONDER_GUARD = `
AUTO-RESPONDER GUARD:
If the user's message appears to be an out-of-office auto-reply, automated acknowledgment, or system error (e.g., "Thank you for messaging us", "We are currently unavailable", "Unknown input type"):
1. Set "canAnswer": false
2. Set "handoff": false
3. Do NOT generate a conversational reply.
`;
Financial & WABA Score Impact (100k Broadcast Sends) Legacy CRMs (WATI / AiSensy / Interakt) +$480 Loop Credit Wastage | WABA Rating: RED Klaros (BYO-WABA + Refusal Gate) $0 Loop Waste | WABA: GREEN
Figure 4: WABA Quality Protection and Zero Markup cost savings in Klaros over 100k broadcast sends.

Competitive Analysis & Technical Benchmark

Platform Broadcast Loop Triage Per-Message Markup Interactive Tap Protection
Klaros Deterministic Refusal State Machine $0 Zero Markup 100% Unconditional Priority
vs WATI None (Loops or Silences) Per-message Markup Added Best-effort
vs AiSensy None (Generative Loop Risk) Per-message Convenience Fee Best-effort
vs Interakt Basic Static Regex Markup Added on Base Rates Best-effort
vs Respond.io Manual Workflow Required Plan Fee + Markup Workflow-dependent
vs SleekFlow Basic Static Keyword Per-User Fee + Markup Workflow-dependent
vs Twilio Raw API (Developer builds) Per-message Fee Custom Code Required

Empirical Verification & Test Proofs

Every assertion in this note is backed by automated test suites running against real SQLite harnesses and Cloudflare D1 mocks:

Verification Test Commands
# Run broadcast autoresponder loop triage test suite
npx mocha test/uni-cloud-broadcast-autoresponder.test.mjs

# Run correspondent self-match verification
npx mocha test/uni-cloud-correspondent-self-match.test.mjs

# Run full project test suite (9,411 passing tests)
npm test

Run WhatsApp campaigns without bot loop panic

Connect your own WhatsApp Business Account with zero per-message markup, full consent ledgers, and deterministic loop protection.

Questions people ask about broadcast auto-responder loop suppression

Why do WhatsApp broadcasts trigger auto-responder loops?

When a mass promotional broadcast is sent to thousands of contacts, handsets with out-of-office auto-reply enabled respond automatically. Naive CRMs treat these inbound texts as human messages and trigger auto-replies or AI model responses, starting an infinite messaging loop.

Does Meta WhatsApp Cloud API supply an is_bot header?

No. Unlike email (RFC 3834 Auto-Submitted) or Slack/Telegram APIs, Meta's Cloud API webhook delivers identical message structures regardless of whether the sender is a human or an auto-reply app.

How does Klaros prevent auto-responder loops without silencing human replies?

Klaros uses a 3-tier state machine (HUMAN, SUSPECTED_AUTOMATED, AUTOMATED) with dynamic gap thresholding (15s gap, 60-character threshold for broadcast context) and multilingual pattern matching, while granting 100% unconditional immunity to quick-reply button taps and WhatsApp Flow submissions.

How does zero-markup messaging save costs during mass broadcasts?

Traditional BSPs (WATI, AiSensy, Interakt) charge per-message markups or convenience fees on top of Meta rates, profiting from runaway bot loops. Klaros is a zero-markup BYO-WABA platform, meaning you pay Meta direct rates with zero per-message fee and zero wasted loop credits.

Written 23 September 2026. Klaros — Built to Remember Your Story. Related: our philosophy, reliability notes, all build notes.