rt class StochasticDispatcher {
private readonly config: DispatchConfig;
private messageTimestamps: number[] = [];
constructor(config: DispatchConfig) {
this.config = config;
}
async scheduleDelivery(payload: string): Promise<void> {
const now = Date.now();
// Enforce rate limit window
this.messageTimestamps = this.messageTimestamps.filter(t => now - t < 60000);
if (this.messageTimestamps.length >= this.config.maxMessagesPerMinute) {
throw new Error('Rate limit exceeded. Backing off.');
}
const jitter = randomInt(0, this.config.maxJitterMs);
const delay = this.config.baseDelayMs + jitter;
await new Promise(resolve => setTimeout(resolve, delay));
this.messageTimestamps.push(Date.now());
// Delegate to WA API client
await this.transmit(payload);
}
private async transmit(payload: string): Promise<void> {
// Implementation specific to your WA provider
console.log([TRANSMIT] ${new Date().toISOString()} | ${payload});
}
}
**Architecture Rationale:** Rate limiting is handled at the dispatcher level, not the API client. This prevents cascading failures and ensures the platform never sees burst patterns. The jitter range is calibrated to mimic human typing and reading delays.
### 2. Hard Opt-Out Interception Layer
Compliance cannot be delegated to the LLM. Natural language models are probabilistic and may ignore or misinterpret opt-out requests. A deterministic guard must intercept keywords before any business logic executes.
```typescript
export class ComplianceGuard {
private readonly blocklist: Set<string> = new Set([
'stop', 'cancel', 'optout', 'unsubscribe', 'parar', 'sair', 'cancelar'
]);
constructor(private readonly storage: { revokeAccess: (phone: string) => Promise<void> }) {}
async evaluate(phone: string, rawInput: string): Promise<boolean> {
const normalized = rawInput.toLowerCase().trim();
const isOptOut = this.blocklist.has(normalized) || normalized.includes('stop all');
if (isOptOut) {
await this.storage.revokeAccess(phone);
return false; // Halt processing
}
return true; // Proceed to LLM/Agent
}
}
Architecture Rationale: Opt-out handling is synchronous and immediate. The guard queries a persistent store to cancel pending queue items and updates the contact state atomically. This prevents the "zombie message" problem where queued notifications continue after a user requests cessation.
3. 24-Hour Window & Template Router
WhatsApp enforces a strict session window. Outside the 24-hour window initiated by the user, only pre-approved templates may be sent. Routing logic must validate temporal boundaries before API calls.
export class WindowRouter {
constructor(private readonly db: { getLastInteraction: (phone: string) => Promise<Date | null> }) {}
async determineRoute(phone: string): Promise<'session' | 'template'> {
const lastInteraction = await this.db.getLastInteraction(phone);
if (!lastInteraction) return 'template';
const windowMs = 24 * 60 * 60 * 1000;
const elapsed = Date.now() - lastInteraction.getTime();
return elapsed < windowMs ? 'session' : 'template';
}
}
Architecture Rationale: Separating routing from transmission prevents error 131047 from polluting logs and degrading health scores. The router acts as a policy decision point (PDP), ensuring the correct message type is selected before network I/O occurs.
4. Session Isolation & Fingerprint Vault
For on-premise or self-hosted bridges, session state must never be shared across numbers. IP consistency and isolated authentication contexts are mandatory.
export class SessionVault {
private readonly sessions: Map<string, SessionContext> = new Map();
async provision(number: string, config: SessionConfig): Promise<void> {
if (this.sessions.has(number)) {
throw new Error('Session already active. Parallel instances forbidden.');
}
this.sessions.set(number, {
id: number,
authPath: `./vault/${number}/auth.json`,
ipBinding: config.ipAddress,
createdAt: Date.now()
});
}
async teardown(number: string): Promise<void> {
const ctx = this.sessions.get(number);
if (ctx) {
// Graceful disconnect, clear memory, rotate credentials
this.sessions.delete(number);
}
}
}
interface SessionConfig { ipAddress: string; }
interface SessionContext { id: string; authPath: string; ipBinding: string; createdAt: number; }
Architecture Rationale: Each number operates in a dedicated process boundary. Shared state or IP rotation during an active session triggers anti-fraud heuristics. The vault enforces one-to-one mapping between identifier and runtime context.
5. Health Telemetry & Circuit Breaker
Proactive monitoring prevents silent degradation. A background worker should poll Meta's Graph API and trigger a circuit breaker when health status changes.
import axios from 'axios';
export class HealthTelemetry {
constructor(
private readonly wabaId: string,
private readonly token: string,
private readonly circuitBreaker: { trip: () => void; reset: () => void }
) {}
async poll(): Promise<void> {
const endpoint = `https://graph.facebook.com/v20.0/${this.wabaId}`;
try {
const res = await axios.get(endpoint, {
params: { fields: 'health_status', access_token: this.token }
});
const status = res.data.health_status?.can_send_message;
if (status === 'LIMITED') {
this.circuitBreaker.trip();
console.warn('[HEALTH] Proactive messaging suspended. Status: LIMITED');
} else if (status === 'AVAILABLE') {
this.circuitBreaker.reset();
} else if (status === 'BLOCKED') {
this.circuitBreaker.trip();
console.error('[HEALTH] Number blocked. Manual Meta intervention required.');
}
} catch (err) {
console.error('[HEALTH] Telemetry fetch failed:', err);
}
}
}
Architecture Rationale: Health monitoring is decoupled from message flow. The circuit breaker pattern ensures that when Meta signals degradation, the system halts outbound campaigns immediately. No retries, no exponential backoff loops. Aggressive reconnection is a primary vector for permanent bans.
Pitfall Guide
| Pitfall | Explanation | Fix |
|---|
| Fixed-Interval Scheduling | Dispatching messages at exact intervals (e.g., every 30s) creates a machine signature. Meta's anomaly detection flags this as automated blast behavior. | Implement stochastic jitter (±40% variance) and randomize queue consumption order. Align dispatch windows with recipient timezones. |
| LLM-Driven Compliance | Delegating opt-out or policy checks to the language model introduces latency and inconsistency. Models may hallucinate permissions or ignore keywords. | Place a deterministic regex/set-based guard before the LLM pipeline. Compliance is a hard boundary, not a probabilistic output. |
| Blind Retry on 401/403 | Automatically retrying failed API calls without analyzing the error code amplifies rate limit violations and triggers permanent suspension. | Implement a circuit breaker. On 401/403, halt immediately, log the event, and require manual review or cooldown period before resuming. |
| Cross-Number Session Sharing | Reusing authentication tokens or IP addresses across multiple WhatsApp numbers confuses Meta's device fingerprinting. The platform treats this as account takeover. | Enforce strict process isolation. Each number requires dedicated auth storage, unique IP binding, and independent runtime lifecycle. |
| Ignoring the 24h Window | Attempting to send free-form messages outside the customer-initiated window generates error 131047. Repeated violations degrade the health score silently. | Route all outbound traffic through a temporal validator. Outside the window, enforce template-only delivery with pre-approved content. |
| Silent Health Degradation | Assuming the API is healthy until a ban occurs leaves no recovery window. Health scores decay gradually; reactive fixes are too late. | Poll the Graph API health endpoint every 5–10 minutes. Trigger automated campaign pauses when status shifts to LIMITED. |
| Template Bypass Attempts | Trying to circumvent template approval by splitting messages or using invisible characters triggers content policy filters. | Submit templates through the official Manager interface. Use approved variables for dynamic content. Never attempt to mask unapproved text. |
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| High-volume marketing campaigns | Cloud API + Template-only routing | Guarantees compliance, avoids session window violations, scales predictably | Moderate (per-message pricing) |
| Customer support with 24h replies | Cloud API + Session messaging | Leverages free window, reduces template costs, maintains conversational flow | Low (within window) |
| Legacy on-premise integration | Self-hosted bridge + Session Vault | Full control over infrastructure, but requires strict fingerprint management | High (infra + maintenance) |
| Multi-brand number management | Dedicated process per number + Health Telemetry | Prevents cross-contamination, isolates reputation risk per brand | Moderate (additional compute) |
Configuration Template
governance:
dispatch:
base_delay_ms: 2000
max_jitter_ms: 1500
max_per_minute: 20
timezone_enforcement: true
compliance:
opt_out_keywords: ["stop", "cancel", "optout", "parar", "sair"]
hard_block: true
audit_log_path: "/var/log/wa-governance/audit.json"
routing:
window_hours: 24
fallback_to_template: true
template_error_code: 131047
telemetry:
poll_interval_seconds: 300
circuit_breaker:
on_limited: "pause_proactive"
on_blocked: "halt_all"
retry_policy: "none"
sessions:
isolation_mode: "strict"
ip_binding: "dedicated"
vault_path: "./vault"
Quick Start Guide
- Initialize the Governance Layer: Clone the repository, install dependencies, and configure the
governance.yaml file with your WABA ID and access token.
- Provision Session Isolation: Run the session vault initializer to create dedicated auth directories and bind IP addresses for each target number.
- Deploy the Compliance Guard: Integrate the opt-out interceptor into your message ingestion pipeline. Verify that keywords halt processing before reaching the LLM.
- Activate Health Telemetry: Start the background polling service. Confirm that the circuit breaker triggers correctly when simulating a
LIMITED health status.
- Validate with Stochastic Dispatch: Run a dry-run campaign using dummy payloads. Monitor logs to confirm jitter variance, window routing, and audit trail generation before connecting to production numbers.
WhatsApp automation at scale is not an API integration problem; it is a behavioral governance challenge. By treating the platform's health score as a first-class architectural constraint, you preserve your communication asset, maintain deliverability, and build systems that survive production pressure.