Chatwoot WhatsApp Integration

Choose the safe Chatwoot migration path for customer-owned and Dualhook Meta apps across Chatwoot Cloud and self-hosted deployments.

This guide covers the API security upgrade for Chatwoot integrations. The correct path depends on which Meta app authorizes your outbound WhatsApp requests. Identify that first; do not replace a working credential as a test.

Choose Your Path

Current authorizationChatwoot deploymentRequired action
Your company owns the Meta app and Meta access token used by ChatwootCloud or self-hostedKeep the native WhatsApp Cloud inbox pointed at graph.facebook.com. Do not replace the Meta token with a Dualhook key.
Dualhook's Meta app authorizes outbound operationsChatwoot CloudDo not put a dh_live_... key in the native WhatsApp token field. Use a customer-owned Meta app or a fully implemented API-channel bridge.
Dualhook's Meta app authorizes outbound operationsSelf-hosted ChatwootDeploy and test a per-inbox Dualhook provider adapter before changing the credential. Changing only WHATSAPP_CLOUD_BASE_URL is not sufficient.

If you are unsure which case applies, check who created and controls the permanent Meta token currently stored in the Chatwoot inbox. A token created from your own Meta app or system user remains a customer-owned Meta credential. A connection-scoped dh_live_... key is a Dualhook credential and is valid only at api.dualhook.com.

Customer-Owned Meta App

This is the safest and lowest-maintenance Chatwoot configuration. If it is already working, the API Upgrade page requires no Chatwoot change. Continue to use:

https://graph.facebook.com
Authorization: Bearer <YOUR_META_ACCESS_TOKEN>

Do not replace the token with dh_live_..., and do not redirect the inbox to Dualhook. The Dualhook runtime migration applies only to operations authorized through Dualhook's Meta app.

For a new or migrated native inbox:

  1. In your Meta business, confirm that your app and system user have access to the WABA and Phone Number ID.
  2. Create a permanent system-user token with the WhatsApp permissions required by Chatwoot.
  3. In Chatwoot, use the manual WhatsApp Cloud setup and enter the display phone number, Phone Number ID, WABA ID, and your Meta token.
  4. Keep the native Meta Graph endpoint. Do not enter a Dualhook runtime key.
  5. Configure the callback and verification token shown by Chatwoot, or preserve an existing working callback while you test the customer-owned app subscription.
  6. Test inbound text and media, an in-window reply, a template send, an outbound attachment, delivered/read statuses, and template synchronization.
  7. Remove an obsolete Dualhook Webhook Override only after the customer-owned app has passed all inbound and outbound tests.

When converting an existing Chatwoot Embedded Signup inbox, prefer Chatwoot's documented migration to manual setup instead of deleting the inbox. Chatwoot states that its migration preserves the inbox and its conversations and settings.

Dualhook Meta App

Outbound requests for this case must use the connection's exact v25.0 runtime routes and a bearer-only credential:

POST https://api.dualhook.com/v25.0/<PHONE_NUMBER_ID>/messages
Authorization: Bearer dh_live_...

The runtime is an allowlisted WhatsApp API, not a general Graph proxy. It does not accept Meta tokens, an access_token query parameter, appsecret_proof, app secrets, arbitrary Graph paths, or webhook-management calls. See Runtime API Authentication and the Runtime API Endpoint Reference.

Chatwoot Cloud

There is no safe native credential swap. Chatwoot Cloud does not expose an installation environment variable or custom native WhatsApp provider per inbox. Pasting dh_live_... into its Meta access-token field would cause at least some requests to go to Meta with the wrong credential.

Use one of these options:

  1. Recommended: authorize the number through your own Meta app and use Chatwoot's native manual WhatsApp Cloud inbox.
  2. Use a customer-controlled bridge with a Chatwoot API inbox. The bridge must implement inbound/outbound mapping, media, templates, delivery statuses, retries, idempotency, and conversation continuity. Chatwoot documents the API inbox and callback model.

An API inbox is not a drop-in conversion of a native WhatsApp inbox. Plan how to retain historical conversations and test the bridge with a controlled number or conversation before changing production webhook routing.

Self-Hosted Chatwoot

Use a per-inbox Dualhook provider adapter. Dualhook does not currently claim that unmodified Chatwoot is compatible with a dh_live_... credential.

The adapter must:

  1. Activate only for an explicitly marked Dualhook inbox. Do not redirect every WhatsApp Cloud inbox on the installation.
  2. Build all runtime calls from https://api.dualhook.com/v25.0.
  3. Send dh_live_... only as Authorization: Bearer; remove access_token and appsecret_proof from URLs, query strings, and bodies.
  4. Route text, template, interactive, and attachment sends through the v25.0 /messages route.
  5. Read inbound media metadata from /<MEDIA_ID> and download the returned Dualhook /<MEDIA_ID>/content URL with the same bearer key.
  6. Ensure template synchronization uses bearer authentication and resubmits only Dualhook's safe after or before cursor. The published v4.16.2 release needs both changes; post-release Chatwoot source already uses bearer authentication and an after cursor for routine synchronization, but still needs its v14.0 path normalized. Never allow an absolute paging.next Graph URL.
  7. Validate the configured Phone Number ID and WABA ID with bounded single-object reads. Do not call the WABA /phone_numbers collection.
  8. Make health calls provider-aware, bearer-authenticated, and limited to fields supported by the runtime.
  9. Adapt Chatwoot CSAT template create, status, and delete operations to the v25.0 template routes.
  10. Skip Chatwoot-managed phone registration/deregistration, app subscription, webhook callback override, callback clearing, and Embedded Signup token exchange. Those remain Dualhook control-plane operations.
  11. Keep WhatsApp Calling disabled unless every route used by the deployed Chatwoot version has been separately certified.

Do not use the global WHATSAPP_CLOUD_BASE_URL as the production solution, especially when the installation also hosts inboxes that legitimately use customer-owned Meta tokens.

Why the Base URL Alone Is Not Enough

The published Chatwoot v4.16.2 release contains a global WHATSAPP_CLOUD_BASE_URL, but its WhatsApp implementation is not a single interchangeable Graph client. Chatwoot's develop branch changed routine template synchronization shortly after the release without changing the application version string, so audit the exact commit you deploy:

Chatwoot behaviorDualhook requirementResult without an adapter
Text/template/interactive sends use v13.0; attachments use v24.0Exact v25.0 routesRequests are rejected or misrouted.
Inbox validation uses v14.0 and puts access_token in the URL. The published v4.16.2 tag also does this for routine template sync.Bearer-only dh_live_...Validation is incompatible and the key may enter URL logs.
The published v4.16.2 tag follows an absolute template paging.next URL. Post-release source uses the after cursor safely.Safe cursor pagination onlyThe release tag needs adaptation; newer source still needs its template path normalized to v25.0.
Inbox validation calls /<WABA_ID>/phone_numbersBounded Phone Number ID and WABA readsThe collection is outside the runtime allowlist.
Health uses a hardcoded graph.facebook.com base and query tokenDualhook v25.0 plus bearer authHealth calls still go to Meta.
Manual inbox creation attempts registration and webhook setupDualhook owns provisioning and Webhook OverrideSetup invokes unsupported control-plane routes.

These behaviors are visible in Chatwoot's official published v4.16.2 source for the WhatsApp Cloud provider, health service, WhatsApp channel model, and webhook setup service. The later template-management-token change moved routine template synchronization to bearer authentication and cursor-only pagination, but it did not fix Dualhook inbox validation, API-version fragmentation, hardcoded health/setup clients, or lifecycle mutations.

Inbound Webhooks

The outbound credential migration does not require an inbound routing change. Meta can continue sending WhatsApp message and status events directly to the existing Chatwoot callback through Dualhook's Webhook Override:

https://<YOUR_CHATWOOT_HOST>/webhooks/whatsapp/<DISPLAY_PHONE_NUMBER>

Keep the callback URL, display phone number, Phone Number ID, and verification token unchanged during an outbound migration. Do not delete and recreate the inbox merely to change its outbound transport.

There is a webhook-authenticity limitation to understand. In Chatwoot v4.16.2, the WhatsApp webhook controller requires X-Hub-Signature-256 verification for an Embedded Signup inbox or when an applicable app secret is available. A manual WhatsApp Cloud inbox without an app secret can skip POST signature verification. Dualhook will not reveal its Meta app secret as a workaround. Use TLS, an unguessable verification token, rate limits, request-size limits, idempotency, and event monitoring, and account for this gap in your risk review.

Feature Support

ConfigurationInbound and statusesOutbound repliesMediaTemplate syncNative healthExisting inbox/history
Customer-owned Meta app with native ChatwootYesYesYesYesYesYes
Chatwoot Cloud native inbox with dh_live_...Inbound may continueNoNoNoNoPreserved but partially broken
Self-hosted with only WHATSAPP_CLOUD_BASE_URL changedInbound may continueNot reliableNoNoNoYes
Self-hosted per-inbox Dualhook adapterYesYesYesYesYes, after adaptationYes
API inbox plus complete bridgeIf implementedIf implementedIf implementedMust be implementedMust be implementedUsually a new inbox

Safe Migration Order

For a self-hosted adapter or an API-channel bridge:

  1. Record and back up the existing inbox/channel configuration, IDs, webhook URL, verification token, database, and deployment configuration.
  2. Build and test the replacement transport before changing any production credential or callback.
  3. Add tests that fail if a Dualhook inbox contacts graph.facebook.com, sends access_token or appsecret_proof, follows an absolute pagination URL, or invokes registration/webhook-management routes.
  4. Keep the existing Dualhook Webhook Override unchanged.
  5. For a self-hosted adapter, mark the existing inbox as Dualhook-managed and only then replace its outbound credential with the connection-scoped key.
  6. Test inbound and outbound text, reply context, interactive messages, template sends, multi-page template sync, inbound and outbound media, delivered/read/ failed statuses, and health.
  7. Revoke the previous outbound credential only after all tests succeed and no queued job or campaign still depends on it.

Security and Upgrades

  • Store dh_live_... in a server-side secret manager. Chatwoot provider configuration, database replicas, backups, Rails consoles, logs, and support exports must all be treated as secret-bearing systems.
  • Never send the key to graph.facebook.com, put it in a URL, expose it to browser code, or share it in screenshots or support messages.
  • Never request Dualhook's Meta token, Meta app secret, or generated App Secret Proof. Dualhook injects those internally.
  • Re-audit the adapter on every Chatwoot upgrade. Search changed WhatsApp code for graph.facebook.com, access_token, API-version strings, paging.next, setup/ teardown calls, media, health, templates, CSAT, and voice features.
  • Pin the Chatwoot release until the adapter's contract tests pass against a Dualhook test connection and customer-owned Meta inboxes still call Meta.

Sources

This compatibility review was checked against the published Chatwoot v4.16.2 release, marked as the latest Chatwoot release on July 31, 2026, plus post-release source changes through commit bc7ae88d. The published release tag points to 70e284a; repository HEAD still reported application version 4.16.2 while containing later changes. Always identify the exact commit as well as the displayed version. Official sources:

Related

  • WhatsApp Webhook OverrideHow Dualhook uses WhatsApp Webhook Override to route supported customer-path webhooks directly from Meta to your server.
  • Runtime APIUse, protect, rotate, and scope dh_live_ keys for Dualhook's allowlisted Graph-compatible WhatsApp runtime.
  • Runtime API Endpoint ReferenceGraph-compatible message, media, template, health, profile, phone-settings, and QR endpoints for connection-scoped dh_live_ keys.
  • Messaging WebhookReal-time webhook events for inbound messages, delivery statuses, and errors.
  • Sending Template MessagesHow to send template messages via Cloud API with variables, media headers, and URL buttons.
Browse more docsStart Free Trial