DEV Community

Amritasha Agrawal
Amritasha Agrawal

Posted on

Connect WhatsApp to Self-Hosted Chatwoot on Railway (Official Cloud API — Works on the Hobby Plan)

WhatsApp is the reason a lot of teams self-host Chatwoot: one shared inbox where several agents answer the business number, with no per-seat fee. The official way to connect it is Meta's WhatsApp Cloud API, which Chatwoot supports natively.

Good news if you're on Railway's Hobby plan: unlike email, WhatsApp works there. Railway blocks outbound SMTP below Pro, but the Cloud API is plain HTTPS in both directions — Chatwoot calls Meta's Graph API, and Meta calls a webhook on your Chatwoot domain.

This guide covers the setup and, more importantly, three self-hosting details that let Chatwoot show "connected" while incoming messages never appear.

Before you start

You need:

  • A self-hosted Chatwoot reachable over public HTTPS (the default *.up.railway.app domain is fine).
  • A Meta Business portfolio and a Meta developer app with the WhatsApp use case.
  • A phone number that is not currently active in the WhatsApp or WhatsApp Business phone app. Chatwoot's manual setup doesn't support coexistence with the app.
  • Admin access in Chatwoot.

Two things to know about the official API:

  • The 24-hour window. You can reply freely within 24 hours of the customer's last message. After that, Meta only lets you start the conversation again with an approved message template. Chatwoot syncs approved templates from your WhatsApp Business Account automatically.
  • Unofficial bridges are a different thing. Tools that drive WhatsApp Web (WAHA, Baileys-based APIs) skip Meta's approval process, but they're unofficial and numbers can get banned. This guide uses the official API only.

The three self-hosting details that break WhatsApp

1. FRONTEND_URL becomes your webhook URL

Chatwoot builds the WhatsApp webhook from FRONTEND_URL:

<FRONTEND_URL>/webhooks/whatsapp/<phone-number>
Enter fullscreen mode Exit fullscreen mode

and registers that URL with Meta when you create the inbox. If FRONTEND_URL is wrong, empty, or still the Railway domain after you moved to a custom domain, Meta delivers messages to the wrong place and nothing arrives.

Set FRONTEND_URL to the public HTTPS URL agents actually use before creating the inbox. If you change domains later, update FRONTEND_URL, redeploy, and re-run the webhook setup from the inbox (or update the callback URL in your Meta app).

2. Sidekiq must be running

When Meta calls the webhook, Chatwoot's web server accepts the request and hands it to a background job (Webhooks::WhatsappEventsJob, on the low queue). Sidekiq is what turns that job into a conversation.

So if Sidekiq isn't running, Meta sees a successful delivery, Chatwoot looks healthy, and no messages appear — they're sitting in Redis. Before debugging Meta settings, check that Sidekiq is alive: super-admins can open /monitoring/sidekiq and look for a growing low queue.

3. Media needs persistent storage

Photos, voice notes and documents customers send are downloaded and stored through Rails Active Storage. With the default local storage and no volume, they vanish on the next redeploy and old conversations fill with broken images. Mount a volume at /app/storage, or configure S3/R2, before you go live.

Step-by-step: manual Cloud API setup

This is Chatwoot's "manual flow", which works on every self-hosted install.

1. Start the inbox in Chatwoot. Go to Settings → Inboxes → Add Inbox → WhatsApp and choose WhatsApp Cloud. If a Quick setup screen appears, use the manual setup flow link at the bottom.

2. Prepare the Meta app. In Meta for Developers, create (or open) an app, choose the use case for connecting with customers through WhatsApp, and select the business portfolio that owns the number.

3. Copy two IDs. In the app's WhatsApp API Setup page, register your number if needed, then copy the Phone Number ID and the WhatsApp Business Account ID. They're different numbers — mixing them up is the most common error. Paste both into Chatwoot and continue.

4. Create a permanent token. Temporary tokens from the API Setup page expire within a day; use a system user instead:

  • Meta Business Settings → Users → System users → create an admin system user (or reuse one).
  • Assign it your Meta app and your WhatsApp Business Account with full control.
  • Generate token → select the app → expiry Never → permissions whatsapp_business_management and whatsapp_business_messaging.
  • Copy it immediately (Meta shows it once) and paste it into Chatwoot as the permanent access token.

5. Verify and create. Chatwoot checks number access, template access, the webhook callback and the webhook subscription, and configures the webhook in Meta for you. When all four show Verified, create the inbox and add agents.

If the webhook steps fail, use Retry webhook setup. If they keep failing, the system user probably lacks full control of the WhatsApp Business Account. As a fallback, copy the Webhook URL and Verification Token from Chatwoot, paste them under WhatsApp → Configuration in your Meta app, verify, and subscribe to the messages field.

If template access shows as not verified, the token is missing whatsapp_business_management: messages still work, templates won't sync.

Optional: Quick setup (embedded signup)

Chatwoot Cloud customers get a one-click Connect with WhatsApp Business button. Self-hosted installs can enable it too, but only after the instance admin configures their own Meta app for it. In Super Admin → App Config → WhatsApp Embedded set:

  • WHATSAPP_APP_ID — your Meta app ID
  • WHATSAPP_CONFIGURATION_ID — from Facebook Login for Business → Configurations, using the WhatsApp Embedded Signup variation
  • WHATSAPP_APP_SECRET — App Settings → Basic

The button stays hidden until all three are set. It's worth it if you'll onboard several numbers or clients; for one number, the manual flow is quicker.

Testing it

  1. From a personal phone, message the business number.
  2. The conversation should appear in Chatwoot within seconds.
  3. Reply from Chatwoot; the message arrives on the phone.
  4. Send a photo, then redeploy the Chatwoot service and check the photo still loads.

Nothing arrived? Check in this order: Sidekiq is running → FRONTEND_URL matches your public domain → the Meta app's webhook is subscribed to messages → the token hasn't been revoked (update it in the inbox's Configuration tab if you replaced it).

Running Chatwoot on Railway for this

If you don't have a Chatwoot instance yet, I maintain a Railway template that's set up for exactly this: Chatwoot with Sidekiq supervised in the same container, Postgres with pgvector, Redis, and a volume at /app/storage so WhatsApp media survives redeploys. WhatsApp, live chat and the other social channels work on the Hobby plan; email notifications need Pro.

Deploy on Railway

(Disclosure: I earn a share of Railway usage from deployments of this template. Everything above applies to any self-hosted Chatwoot.)

For the rest of the setup — email, storage, upgrades, backups — see Self-Hosting Chatwoot on Railway: What Actually Breaks.

Checklist

  • [ ] FRONTEND_URL is your final public HTTPS domain before creating the inbox
  • [ ] Sidekiq running (check /monitoring/sidekiq)
  • [ ] Volume at /app/storage or S3/R2 for media
  • [ ] Number not active in the WhatsApp phone app
  • [ ] Phone Number ID and WhatsApp Business Account ID not swapped
  • [ ] Permanent system-user token, expiry Never, both WhatsApp permissions
  • [ ] All four checks Verified; webhook subscribed to messages
  • [ ] Templates approved for messaging customers after 24 hours

Questions or something out of date? Say so in the comments — I'll update it.

Top comments (0)