Skip to main content
Pilot Status has a native Chatwoot integration: connect any WhatsApp number to a Chatwoot account and conversations sync bidirectionally — inbound WhatsApp messages appear in Chatwoot, and agent replies in Chatwoot are delivered on WhatsApp.
Pilot Status Chatwoot page

The Chatwoot page — connection status, mirroring toggle, inbox/account IDs, reconnect, and credential update.

Prerequisite: FRONTEND_URL (self-hosted Chatwoot)

If you run a self-hosted Chatwoot, its FRONTEND_URL environment variable must be set before connecting:
  • Use the exact public URL (same scheme/host as the browser), no trailing slash, reachable from the internet — never localhost or an internal IP.
  • Restart both the web and sidekiq containers afterwards (Rails derives default_url_options[:host] from it at boot).
Without FRONTEND_URL, Chatwoot cannot build absolute attachment URLs or valid outgoing webhooks. Symptoms in Pilot Status: messages marked “Failed to send”, media never delivered, and ArgumentError: Missing host to link to! in the Chatwoot/sidekiq logs.
To validate: send a message with an attachment in a test conversation — no Missing host to link to! in the logs, the message shows as sent in Pilot Status, and the media arrives on WhatsApp.

Set up the integration

The integration is configured per number on the Chatwoot page of the Pilot Status dashboard.
1

Gather three credentials from Chatwoot

  • Instance URL — your Chatwoot base URL (e.g. https://chatwoot.your-domain.com or https://app.chatwoot.com).
  • Account ID — the number in your Chatwoot URL after /app/accounts/.
  • User Access Token — from your Chatwoot profile settings.
2

Connect in Pilot Status

Open the Chatwoot page in the dashboard, select the WhatsApp number, and paste the three values. Pilot Status automatically creates the inbox and the webhook in your Chatwoot account — you never paste a webhook URL manually. Unofficial (web/QR) numbers get an API inbox (message mirror); official Meta Cloud numbers get a native whatsapp_cloud inbox — see the prerequisite below.
3

Talk

Inbound WhatsApp messages (including media) open conversations in the new inbox; agent replies flow back to WhatsApp. Message history and delivery status stay in sync.
Official (Meta Cloud API) numbers require WHATSAPP_CLOUD_BASE_URL. For an official number, Pilot Status creates a native whatsapp_cloud inbox (pre-filled with the phone number ID, WABA ID and your ps_ key) so Chatwoot talks to Meta through the Pilot Status layer. Your self-hosted Chatwoot’s global WHATSAPP_CLOUD_BASE_URL must point at the Pilot Status Meta layer before you connect:
Left at the default (graph.facebook.com), Chatwoot validates your ps_ key against Meta directly and the connect fails with “invalid credentials” / a 422 (“Chatwoot rejected the WhatsApp Cloud channel”). This variable is global in Chatwoot: if the same server also runs direct Meta Cloud inboxes (each with its own Meta token), pointing it at Pilot Status breaks them — use a dedicated Chatwoot instance for Pilot Status numbers. Concretely, a re-pointed inbox whose channel API key is a raw Meta token (not a ps_ key) fails silently on send: the layer rejects it with a 401 and Chatwoot leaves the reply stuck with a clock icon and no delivery. Every whatsapp_cloud inbox routed through the layer must use the number’s ps_ key as its channel API key. Chatwoot Cloud (app.chatwoot.com) cannot set this variable and is not supported for official numbers. Full setup: Chatwoot Voice Calls → Step 1.

Channel mode: API mirror vs native Cloud

Every number connects in one of two modes. Pick it in the Channel mode selector on the Chatwoot page (default Automatic):
  • API mirror (Model A) — Pilot Status mirrors messages and media into a Chatwoot API inbox. Chat only (no native voice). It does not depend on WHATSAPP_CLOUD_BASE_URL, so it works on a shared Chatwoot.
  • Native WhatsApp Cloud (Model B) — Chatwoot talks to Meta directly through the Pilot Status layer using a native whatsapp_cloud inbox. Enables native voice/calling and native messaging (no mirror). Requires a dedicated Chatwoot with WHATSAPP_CLOUD_BASE_URL pointed at the layer (see the warning above).
  • Automatic (default) — official numbers → native; unofficial numbers → mirror.
Both modes deliver text and media (image, audio, video, document). Choose by three questions: is the number official or unofficial, is the Chatwoot shared or dedicated, and do you need native voice.
Switching mode = reconnect = new inbox. Changing the mode of an already-connected number creates a new inbox in Chatwoot; history is re-mirrored and old conversation mappings reset (open conversations don’t migrate). Pick the mode before connecting when you can.

Send history to Chatwoot

Coexistence Meta numbers (a WhatsApp Business App migrated onto the platform via Embedded Signup) import ~30 days of chat history on connect. That history shows up in the dashboard /chat view but is not mirrored to Chatwoot automatically. On the Chatwoot page, the Sync history to Chatwoot button replays that already-stored history into the connected inbox.
  • Both channel modes (coexistence numbers). Works whether Chatwoot is connected as an API mirror (Model A) or a native whatsapp_cloud inbox (Model B). The native replay is inbound-only — the customer’s incoming history is replayed; the business’s own outbound replies aren’t re-injected (they never appeared in the native inbox anyway) — and is best run in a maintenance window, since it can briefly notify agents as conversations are (re)created.
  • Order preserved, date in the body. Messages are posted oldest-first. Because Chatwoot stamps every posted message at import time (it does not accept a backdated timestamp), the original date/time is prefixed into each message body — [dd/mm/yyyy hh:mm], in the tenant’s timezone.
  • Idempotent. Re-clicking is safe — it only re-posts messages that never landed.
  • On finish (API mirror mode), the touched conversations are marked read and resolved so agents aren’t flooded with notifications. Native mode can’t mark-read automatically — run it in a maintenance window.
  • Old media whose Meta reference has already expired may import as a [mídia] placeholder (media bytes are captured to storage at import time, so recent media survives).
Via the public API / MCP: POST /v1/chatwoot/history-sync (number-scoped key), or the MCP tools chatwoot_history_replay (start), chatwoot_history_replay_get (progress), and chatwoot_history_replay_cancel.

Pause or disconnect

On the same Chatwoot page you can pause the sync per number (a toggle — nothing is deleted; flip it back to resume) or disconnect the integration entirely.

Voice calls

Voice calls and the messaging inbox above are separate surfaces — WhatsApp call events are not posted into the mirrored conversations. There are two ways to work with calls:
  • Handle calls in the Pilot Status panel (shipped, no setup). The dashboard /chat page has a built-in softphone that answers and places WhatsApp voice calls for both connection types — Official (Meta Cloud API) numbers over WebRTC and unofficial (Pilot Status web) numbers over a server-side audio session. This is the fastest way to take calls right next to your conversations. See Voice Calls.
  • Answer calls natively inside Chatwoot (advanced, self-hosted). Chatwoot v4.15+ ships a native voice channel that can be routed through Pilot Status for Meta Cloud API numbers: agents get a ringing call banner in the Chatwoot inbox and talk over WebRTC, while your Meta token stays with Pilot Status. Full validated walkthrough: Chatwoot Voice Calls. Chatwoot Cloud (app.chatwoot.com) and unofficial numbers are not supported for this path.