Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DevMauricio03/n8n-whatsapp-ai-agent/llms.txt

Use this file to discover all available pages before exploring further.

When something stops working, the fastest first step is always to check the overall health of the stack before diving into a single service. Container statuses and recent log output will usually point to the root cause within seconds. From there, use the service-specific diagnostics below to narrow down the exact failure.
Always check the combined log stream first before investigating individual services. This single command tails the last 50 lines from every container and keeps following new output:
docker compose --env-file .env -f docker/docker-compose.yml logs --tail=50 --follow
Run these three commands in order to determine whether the problem is DNS, container state, or Caddy configuration:
getent hosts "$N8N_DOMAIN"
docker compose --env-file .env -f docker/docker-compose.yml ps
docker compose --env-file .env -f docker/docker-compose.yml logs --tail=100 caddy
Common causes:
  • DNS propagation not complete — the domain’s A record may not have propagated yet. Wait a few minutes and retry getent hosts.
  • Ports 80 or 443 blocked — the host’s firewall or cloud security group may be blocking inbound HTTP/HTTPS traffic. Verify that both ports are open.
  • Wrong domain in .env — confirm that N8N_DOMAIN and CHATWOOT_DOMAIN match the actual DNS records exactly, including any subdomain prefix.
  • Caddy cannot reach the internet — Caddy must be able to reach Let’s Encrypt to issue TLS certificates. If the server has outbound restrictions, check that port 443 outbound is permitted.
  • Port conflict on the host — another service may already be listening on port 80 or 443. Run ss -tlnp | grep -E ':80|:443' to check.
Check each Chatwoot process separately, as initialization (chatwoot-prepare) and the worker (chatwoot-sidekiq) fail for different reasons:
docker compose --env-file .env -f docker/docker-compose.yml logs --tail=200 chatwoot-prepare
docker compose --env-file .env -f docker/docker-compose.yml logs --tail=200 chatwoot-rails
docker compose --env-file .env -f docker/docker-compose.yml logs --tail=200 chatwoot-sidekiq
Checklist:
  • PostgreSQL — confirm the postgres container is healthy and that the chatwoot database exists.
  • Redis — confirm the redis container is running and that REDIS_PASSWORD matches across all services.
  • SECRET_KEY_BASECHATWOOT_SECRET_KEY_BASE must be set to a long, random value. An empty or short value will cause the Rails app to refuse to start.
  • Image version — if you recently changed CHATWOOT_IMAGE, verify the new tag is accessible and compatible with the existing database schema.
  • Pending migrations — if chatwoot-prepare exited with a migration error, the database may need a manual rails db:migrate before the application container will start cleanly.
Verify that the connection parameters in your n8n PostgreSQL credential match the values in .env exactly:
  • Host: postgres (the Docker service name, not localhost).
  • Port: 5432.
  • Database for n8n internals: n8n.
  • Database for workflow queries: agent.
  • User and password: must match POSTGRES_USER and POSTGRES_PASSWORD in .env.
Use this command to confirm that PostgreSQL is accepting connections from inside the network:
docker compose --env-file .env -f docker/docker-compose.yml \
  exec postgres sh -lc 'pg_isready -U "$POSTGRES_USER" -d n8n'
If pg_isready succeeds but n8n still cannot connect, double-check the credential saved inside n8n — the host field must be postgres, not 127.0.0.1 or the public IP.
All three consumers — the Redis server configuration, Chatwoot, and the n8n Redis credential — must use exactly the same password. A mismatch anywhere causes NOAUTH or WRONGPASS errors.Set REDIS_PASSWORD consistently in .env, then verify connectivity:
docker compose --env-file .env -f docker/docker-compose.yml \
  exec redis sh -lc 'redis-cli -a "$REDIS_PASSWORD" PING'
A successful response is PONG. If you see an authentication error, the password the shell is reading does not match the password Redis was started with. Restart the Redis container after correcting .env to apply the change.
Work through this checklist in order — most webhook failures are caused by one of these five conditions:
  • Workflow is not active — in the n8n editor, confirm the workflow is toggled to “Active”. Inactive workflows only respond to /webhook-test/ URLs, not production /webhook/ URLs.
  • Wrong URL prefix — production webhooks use /webhook/<path>. The /webhook-test/ prefix is for manual testing inside the editor only. Update the URL configured in Chatwoot or Meta accordingly.
  • Wrong HTTP method or path — confirm the webhook node is configured for POST and that the path segment matches what Chatwoot is sending.
  • Chatwoot is not sending the event — in the Chatwoot admin panel, verify the webhook endpoint is saved correctly and that the expected event type (e.g., message_created) is checked.
  • Routing or execution issue — check Caddy logs for request receipt, then open n8n’s execution history to see whether the workflow was triggered but failed, or was never triggered at all.
Duplicate AI responses typically mean the workflow is being executed more than once per user message. Check these four areas:
  • Only incoming messages enter the automatic flow — the first Switch or IF node after the webhook must filter strictly on message_type == incoming. Outgoing messages generated by the bot itself must be excluded.
  • Duplicate webhook configurations — check whether the same webhook URL has been registered more than once in the Chatwoot admin panel or in the Meta App Dashboard. Each event should fire exactly one webhook per message.
  • Buffer logic — if the workflow uses a buffer (a Redis key or wait node) to batch rapid messages, verify the deduplication key is constructed correctly and that the TTL is shorter than the delay between real messages.
  • Duplicate executions in n8n — open the execution history and filter by the affected conversation ID. If you see two executions started within milliseconds of each other from the same event, the webhook is being called twice upstream.
The handoff mechanism works by writing a Redis key when an agent takes over a conversation. If the AI continues to respond after handoff, the key is either not being set or not being read correctly. Check these four points:
  • The outgoing event creates the key — verify that the workflow branch handling message_type == outgoing (agent-sent messages) is writing the Redis key that marks the conversation as human-handled.
  • Phone number and conversation ID format — the Redis key is usually constructed from the customer’s phone number or Chatwoot conversation ID. Confirm the format used when writing the key exactly matches the format used when reading it.
  • TTL — if the key expires too quickly (or has no TTL), the AI will resume responding before the human agent finishes the conversation. Review the TTL value and align it with your actual handoff duration.
  • Identical key in both nodes — compare the expression used in the “set key” node and the “check key” node character for character. A leading space, case difference, or mismatched field path will cause the check to always return empty.
Audio processing involves several external dependencies that can each fail independently. Work through these checks:
  • Meta token permissions — the WhatsApp Cloud API token must have the whatsapp_business_messaging permission and access to the specific Phone Number ID. Confirm both in the Meta App Dashboard.
  • Graph API version — the version in WHATSAPP_GRAPH_API_VERSION (e.g., v22.0) must match a version that is currently supported by Meta and that includes the media download endpoint you are calling.
  • Download URL and binary file — the media URL returned by the Cloud API is temporary and single-use. Verify that the node downloading the audio file is executing immediately after receiving the URL and is writing the result as a binary file, not as a text string.
  • Accepted audio format — confirm that the transcription service (OpenAI Whisper or equivalent) accepts the codec and container format that WhatsApp delivers (typically OGG/Opus). Convert with ffmpeg if needed.
  • Safe error message for the client — if transcription fails, the workflow must catch the error and reply to the user with a generic message that does not expose internal URLs, token values, or stack traces.
The Google Drive integration reads a spreadsheet to populate product catalog, pricing, or policy data used by the AI. If the workflow is serving stale data, check the following:
  • Reauthorize OAuth — Google OAuth tokens expire and may be revoked when account permissions change. Open the Google Drive credential in n8n and click “Sign in with Google” to issue a fresh token.
  • File ID — confirm that GOOGLE_DRIVE_SOURCE_FILE_ID in .env matches the ID of the current production spreadsheet. The file ID is visible in the Google Drive URL after /d/.
  • XLSX header row — the workflow reads specific column names from the spreadsheet. If the header row has been renamed, reordered, or has extra spaces, the lookup nodes will return empty values.
  • Manual trigger from Google Drive Trigger — if the workflow uses a Google Drive Trigger node to detect file changes, test it by clicking “Execute Node” manually in the editor to confirm the trigger can reach the file.
  • Restrictions and folio primary key — if the data sheet uses a folio column as the primary key for record lookups, ensure every row has a unique, non-empty folio value and that the query expression in the workflow references the exact column name.
The SQL scripts in the repository (such as the bd_clientes schema) are designed to run automatically when the PostgreSQL Docker volume is created for the first time. They will not re-execute if the volume already exists, even if you restart or recreate the containers.Never delete the production PostgreSQL volume to force the scripts to run again — doing so will permanently destroy all data including Chatwoot conversation history, n8n credentials, and all workflow execution records.To apply a schema change or new migration to an existing installation:
  1. Take a full database backup using pg_dump.
  2. Connect to the postgres container: docker compose exec postgres psql -U "$POSTGRES_USER" -d agent.
  3. Apply the migration SQL manually.
  4. Verify the change, then document it as an applied migration.

Build docs developers (and LLMs) love