AI Automation Intermediate

Bilingual WhatsApp Support Triage: An n8n + WAHA Playbook

Diagnose and recover bilingual WhatsApp support incidents across n8n and WAHA, including errors, hallucinations, sending failures, and handoff issues.

Time needed
45 min
Steps
6
Level
Intermediate

Octacer Team

September 26, 2026 Updated

LinkedIn Post on X
A man in glasses at a dark desk reviewing a support triage workflow on his monitor, a phone with a chat thread beside him, for a bilingual WhatsApp support triage playbook built on n8n and WAHA.

Objective

The target end state is a production workflow that:

Inbound handling

Receives inbound WhatsApp messages through WAHA webhooks
Classifies language (English vs. Spanish) and intent deterministically, with optional AI escalation
Generates responses appropriate to the detected language
Handles delivery failures, message reformatting, and handoff to human agents
Logs every decision with enough context to audit triage quality

Why it matters

The business reason for this implementation: bilingual support teams currently divert engineering or operations staff into manual triage of WhatsApp messages. The system does not replace human agents. It removes the repetitive classification and routing work so agents only handle uncertain, high-risk, or complex cases.

Success criteria

An inbound WhatsApp message is received, classified, and answered or escalated without manual intervention in the standard path
English messages receive English responses; Spanish messages receive Spanish responses
Delivery failures are detected and retried within the configured limits; persistent failures surface to an error channel
Ambiguous messages are escalated to a human agent rather than answered incorrectly
Every decision produces a log entry containing message ID, language result, route taken, and timestamp

Prerequisites

Access

Administrative access to an n8n instance (self-hosted or cloud)
Access to a WAHA instance with an authenticated WhatsApp session
Write access to the database or data store used for logging (SQLite, Postgres, or equivalent)
Access to the notification channel (Slack, email, or similar) used for escalations

Data

A sample set of at least 50 historical bilingual support messages, labeled with correct language and intended route, to validate classification accuracy
The WhatsApp business number's display name and language preference settings

Technical conditions

n8n version that supports webhook triggers and the HTTP Request node
WAHA API endpoint reachable from the n8n instance
WhatsApp session active and connected (not in QR-pending state)
Secrets management available for the WAHA API key; do not store credentials in workflow code

Decisions

Language detection method: Deterministic keyword/character-set detection, an AI model via n8n's LangChain or HTTP node, or a hybrid. Deterministic detection is preferred unless message volume or ambiguity justifies AI. See Step 3.
Escalation channel: Which Slack channel, email address, or ticketing system receives handoff notifications.
Response sender: Whether replies come from the same WAHA session or a separate outbound session.
Production release window: When the workflow will begin receiving live traffic, including confirmation that support staff are ready to handle escalations.

Decisions

Confirm the following before starting implementation. These choices affect configuration later in the playbook.

Tools and systems

  • n8n: Workflow orchestration. Hosts the webhook trigger, classification logic, routing branches, logging nodes, and retry/error handling.
  • WAHA: WhatsApp HTTP API. Provides the webhook for inbound messages, the API for sending replies, and message status events (delivered, failed).
  • Database (SQLite/Postgres): Decision log storage. Records message ID, detected language, route, timestamp, and outcome.
  • Notification channel: Receives escalation alerts when messages route to human review or when delivery persistently fails.

Step 1 — Prepare the WAHA webhook and message log

What this step does

Actions

  1. Confirm the WAHA session is connected. In the WAHA dashboard or via the API, verify the session state is not pending or disconnected.
  2. Identify the webhook URL that WAHA will use. In n8n, create a Webhook node that listens for POST requests at a path such as /whatsapp-inbound. Copy the generated webhook URL.
  3. Configure WAHA to send inbound message events to that URL. Use the WAHA webhook settings or dashboard, and set it to deliver message and message.any events at minimum. Include message.ack events if you want to track delivery status in n8n.
  4. Create the message decision log table in the database:
CREATE TABLE message_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    wa_message_id TEXT NOT NULL UNIQUE,
    phone TEXT NOT NULL,
    message_text TEXT,
    detected_language TEXT,
    route_taken TEXT,
    confidence REAL,
    decision_source TEXT,
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
  1. In n8n, add a Postgres (or SQLite) node that inserts a row into message_log whenever the webhook fires.
  2. Send a test message to the WhatsApp business number.
  3. Verify the webhook fires in n8n and a row is inserted into message_log.

Important considerations

  • The webhook URL must be reachable from WAHA. If n8n is behind a firewall, expose the webhook endpoint or use an n8n tunnel during testing.
  • Do not log the full message body into notifications or error channels. Log it into the database only.
  • WAHA may redeliver events. The UNIQUE constraint on wa_message_id prevents duplicate log rows; handle insert conflicts gracefully in the workflow.

Step 2 — Set up outbound reply sending

What this step does

Actions

  1. In n8n, add an HTTP Request node configured to call the WAHA send-message endpoint.
  2. Set the method to POST and the URL to <WAHA_API_BASE_URL>/api/sendText.
  3. Add the WAHA API key as a header: X-Api-Key: <WAHA_API_KEY>.
  4. Configure the request body:
{
  "session": "<WAHA_SESSION_NAME>",
  "chatId": "{{ $json.phone }}@c.us",
  "text": "{{ $json.replyText }}"
}
  1. Run the workflow with a test payload containing a real recipient phone number and the text Test reply from triage system.
  2. Verify the message arrives on the recipient's WhatsApp.

Important considerations

  • Store the API key in n8n credentials, not as a static header value in the node.
  • WhatsApp requires the @c.us suffix for personal chat IDs. Do not construct it twice.
  • If messages exceed WhatsApp limits, truncate text at the message boundary before sending. WhatsApp will reject or split overlong messages unpredictably.

Step 3 — Build the classification and routing logic

What this step does

Determines the language of the incoming message and decides whether it can be answered automatically or must go to a human. This is the core decision layer of the system.

Choose the language detection model

Use deterministic detection as the default. It is predictable, auditable, and does not require model latency or cost. Deterministic detection works when messages contain enough Spanish-specific character sequences or common words to be unambiguous.

Use AI-based detection when messages are frequently short, mixed-language, or slang-heavy, and the deterministic classifier's error rate is unacceptable in validation.

A hybrid approach may be appropriate: deterministic detection with an AI fallback when confidence is low.

Actions

  1. Add a Function node after the webhook that normalizes the message text.
  2. Implement deterministic language detection. A simplified implementation:
// Simplified example — refine thresholds with real data
const text = $json.messageText.toLowerCase();
const spanishMarkers = [
  'hola', 'gracias', 'por favor', 'buenos días', 'buenas tardes',
  'querría', 'necesito', 'cuándo', 'dónde', 'cuál', 'el', 'la', 'los', 'las',
  'ñ', 'á', 'é', 'í', 'ó', 'ú', '¿', '¡'
];
let score = 0;
for (const marker of spanishMarkers) {
  if (text.includes(marker)) score += 1;
}
const decision = {
  detectedLanguage: score >= 3 ? 'es' : 'en',
  confidence: score >= 3 ? Math.min(score / 5, 0.95) : 0.6,
  decisionSource: 'deterministic'
};
return decision;
  1. If using AI fallback, add an HTTP Request node (or LangChain node) that classifies the message when deterministic confidence is below threshold. Require the model to return a JSON object with language and confidence fields.
  2. Add a Switch node with the following branches:
  • es → Spanish response path
  • en → English response path
  • ambiguous (confidence below threshold, or AI call failed) → escalation path
  1. For each language path, add a "generate response" step. Start with a template-based response that restates the detected intent and sets expectations. Do not attempt free-form AI answering until the template path is proven in validation.
  2. Add an escalation branch that constructs a handoff message containing message ID, phone, detected language, confidence, and the original message text, then sends it to the notification channel.

![IMAGE: fig-1 | Decision-flow diagram showing message text entering language detection, branching on confidence into English path, Spanish path, or ambiguous escalation, with each branch feeding either an automated template reply or a human agent handoff notification]]

Important considerations

Step 4 — Implement the response generation and handoff

What this step does

Actions

  1. Build the English response template. Example:
// Simplified example
const template = `
Hello! We received your message. Our team typically responds within
2 business hours. Your reference number is ${$json.waMessageId}.
`.trim();
  1. Build the Spanish response template:
// Simplified example
const template = `
¡Hola! Recibimos tu mensaje. Nuestro equipo suele responder dentro
de 2 horas hábiles. Tu número de referencia es ${$json.waMessageId}.
`.trim();
  1. Connect the template output to the HTTP Request node from Step 2.
  2. For the escalation path, construct a notification payload containing:
  • wa_message_id
  • phone
  • message_text
  • detected_language
  • confidence
  • reason (e.g., "low confidence", "AI failure")
  1. Send the notification to the escalation channel.
  2. Add a final logging node that updates the message_log row with route_taken and decision_source.

Important considerations

Step 5 — Add delivery confirmation and retry handling

What this step does

Detects and recovers from sending failures so a failed reply does not silently disappear. This is the difference between a demo workflow and a production support tool.

Actions

  1. Configure WAHA to send message.ack events to the n8n webhook (or a second webhook path).
  2. In n8n, create a workflow branch that handles message.ack events. Map ack codes:
  • 1 / 2 → delivered
  • 3 → read
  • Negative or error states → failed
  1. Build a state store for outbound messages. A simplified table:
CREATE TABLE outbound_state (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    wa_message_id TEXT NOT NULL UNIQUE,
    phone TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending',
    attempts INTEGER NOT NULL DEFAULT 0,
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
  1. When a send is initiated, insert a row with status pending.
  2. On message.ack failure, increment attempts. If attempts are below the retry limit (start with 3), re-send the same message content.
  3. If attempts exceed the limit, mark the message as failed and send an alert to the escalation channel containing wa_message_id, phone, and failure information.
  4. Add cleanup: mark messages as delivered when ack confirms delivery, and schedule a sweep (e.g., n8n cron trigger) that flags messages stuck in pending for longer than a threshold (e.g., 10 minutes) as suspicious.

![IMAGE: fig-2 | Sequence diagram showing workflow sending a message, receiving an ack event, retrying on failure up to a limit, then alerting the escalation channel after the final failed attempt]]

Important considerations

Step 6 — Wire the paths together end-to-end

What this step does

Connects the verified components so the system operates as one workflow rather than separate tested fragments.

Actions

  1. In the main n8n workflow, connect:
  • Webhook → message log insert → normalize → classify → route
  • Each route branch → response generation → send → outbound state insert
  • Ambiguous branch → escalation notification → log update
  1. Connect the ack-handling workflow or branch to the outbound state table.
  2. Add the pending-message sweep cron trigger and connect it to the alert path.
  3. Re-run the full test suite from earlier steps as one continuous run.
  4. Enable the workflow in n8n (activate it) so the webhook accepts live traffic.

Important considerations

Validation

Run the following end-to-end tests against the live workflow. Use a test phone number, not a real customer.

Functional behavior

Send a Spanish message: Hola, necesito ayuda con mi pedido por favor.
Confirm the customer receives a Spanish template reply with a reference number.
Send an English message: I need help with my order.
Confirm the customer receives an English template reply.
Send an ambiguous message: meh or a single emoji.
Confirm no reply is sent and an escalation appears in the notification channel.

Data correctness

Query message_log and confirm each message has the correct detected_language, route_taken, and decision_source.
Confirm outbound_state contains one row per outbound message with status delivered.

Failure behavior

Temporarily disable the WAHA session.
Send a message that triggers a reply.
Confirm the send fails, retries occur, and after the limit an escalation alert fires.
Restore the WAHA session and confirm the next message succeeds.

Repeatability

Send the same message twice.
Confirm two message_log rows with distinct wa_message_id values, and two distinct reference numbers for the customer.

Rollback & edge cases

Rollback

The system consists of n8n workflows, a WAHA session, and database tables. Roll back in this order:

  1. Disable the n8n workflow that handles inbound messages. This immediately stops new message processing. Existing webhook traffic that arrives after deactivation is lost — notify the support team before disabling if live traffic is flowing.
  2. Disable the ack-handling workflow to stop state updates and retries.
  3. Restore prior n8n workflow versions if you exported them before changes. If not, the workflows are recoverable only from manual reconstruction.
  4. Keep the database tables. Dropping message_log or outbound_state is destructive and loses audit data. If you must clear test data, delete only rows with test phone numbers, or run a dedicated cleanup that preserves production records.

Do not disable the WAHA session to roll back. This interrupts all WhatsApp traffic, not just automation.

Edge cases

  • Empty message body: WAHA may deliver messages with no text (images, voice notes). Route these to escalation with reason non-text, not to language classification.
  • Duplicate webhook delivery: WAHA redelivery produces duplicate processing. The UNIQUE constraint on wa_message_id prevents duplicate log rows, but ensure the reply-send step also checks whether a reply was already sent for that ID.
  • Mixed-language message: A message with both English and Spanish may produce a mid-confidence score. Escalate if confidence is below threshold; do not guess.
  • Expired WAHA API key: Sends fail with authentication errors. The retry logic will exhaust attempts and alert. Rotate the key and re-test.
  • WhatsApp session disconnect: Sends fail immediately. The outbound state will accumulate pending rows. Handle by alerting on sustained failure and requiring manual session reconnect.
  • Ack events not arriving: If WAHA is not configured to send message.ack, the sweep job will flag all messages as stuck. Confirm ack delivery in one test before enabling the sweep.
  • Very long messages: WhatsApp truncates. Truncate customer-facing replies at the message boundary and log that truncation occurred.
  • Timezone skew: Webhook timestamps use server time. If escalation alerts need local time, apply the conversion at the notification step, not in the log.

Next step

Topics #playbook #automation #reliability #operations #triage

Finished the playbook? Share it with your team.

LinkedIn Post on X
From playbook to production

Want it running on your stack?

We implement this playbook for clients, tailored to your stack, your data, and your team's workflow, and handed over documented.