Skip to main content

Documentation Index

Fetch the complete documentation index at: https://chatbotx.io/docs/llms.txt

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

Why you need a tunnel

Channel integrations require public HTTPS URLs to:
  • Receive webhook events (incoming messages, postbacks, read receipts)
  • Complete OAuth redirect flows
  • Serve media files that external platforms download when your bot sends attachments
During local development your server runs on localhost, which is not reachable from the internet. A local tunnel creates a public HTTPS URL and forwards traffic back to your machine.

How it works

Webhook from Facebook / WhatsApp / Telegram / ... 
		|
        |  HTTPS POST /integrations/<channel>/webhook

  ngrok tunnel (public)
        |

  builder app  →  localhost:3123
Because file URLs are generated directly from NEXT_PUBLIC_ASSET_URL (pointing to the RustFS object storage), external platforms also need to download files from your local RustFS instance. That requires a second tunnel on port 9000.
Facebook / WhatsApp / ... (downloading an image)
		|
        |  GET https://<rustfs-tunnel>/chatbotx/<file>

  ngrok tunnel (port 9000)
        |

  RustFS  →  localhost:9000

Prerequisites

  • Node.js >= 24 and pnpm 10.x installed
  • Docker services running (docker compose up -d)
  • An ngrok account (the free tier is sufficient)
  • Credentials configured for the channel you are testing (e.g. App ID and App Secret for Messenger, WhatsApp, etc.)

Step 1 — Install ngrok

brew install ngrok/ngrok/ngrok
Authenticate with your token:
ngrok config add-authtoken <YOUR_NGROK_TOKEN>
Your token is on the ngrok Dashboard → Your Auth Token page.

Step 2 — Start the local servers

In separate terminals, start both the builder app and make sure Docker (RustFS) is up:
# Terminal 1 — builder app
pnpm --filter builder dev

# Terminal 2 — Docker services (PostgreSQL, Redis, RustFS, …)
docker compose up -d
Verify before continuing:
  • Builder: http://localhost:3123
  • RustFS: http://localhost:9000

Step 3 — Start two ngrok tunnels

You need one tunnel for the builder app and one for RustFS (file storage).
ngrok http 3123
Each command will print a forwarding URL. Note both of them:
Forwarding  https://abcd-1111.ngrok-free.app -> http://localhost:3123  ← builder
Forwarding  https://efgh-2222.ngrok-free.app -> http://localhost:9000  ← RustFS
With a free ngrok account, tunnel URLs change every time you restart ngrok. You must update all configuration (env vars and channel app settings) each time. To avoid this, enable a static domain (one free static domain is included in the free tier) or use a paid plan.

Step 4 — The three URLs to configure

Replace the example hostnames with your actual tunnel URLs.

Webhook URL

https://abcd-1111.ngrok-free.app/integrations/<channel>/webhook
The <channel> segment matches the integration name, for example:
ChannelWebhook URL
Facebook Messenger/integrations/messenger/webhook
WhatsApp/integrations/whatsapp/webhook
Instagram/integrations/instagram/webhook
Telegram/integrations/telegram/webhook
Zalo/integrations/zalo/webhook
The route is public (no authentication required). The platform will:
  • Call GET <webhook-url> with a verify token to confirm ownership
  • Call POST <webhook-url> to deliver events (messages, postbacks, reads, …)

OAuth Redirect URI

https://abcd-1111.ngrok-free.app/integrations/<channel>/callback
Register this URI in your channel app’s Valid OAuth Redirect URIs setting so the platform accepts redirects back to your local environment after the user authenticates.

File URL

https://efgh-2222.ngrok-free.app/chatbotx/
This is the public root of the RustFS bucket that the builder uses to serve uploaded files. When your bot sends an image or attachment, the platform’s servers will download it from this URL.

Step 5 — Update environment variables

Open the .env file at the project root and update these variables:
.env
# ── Builder app ──────────────────────────────────────────────
BETTER_AUTH_URL=https://abcd-1111.ngrok-free.app
NEXT_PUBLIC_BUILDER_URL=https://abcd-1111.ngrok-free.app

# ── RustFS / file storage ────────────────────────────────────
# Public URL used to build asset URLs sent to external platforms
NEXT_PUBLIC_ASSET_URL=https://efgh-2222.ngrok-free.app/chatbotx/

# Internal S3 endpoint — keep pointing to localhost (server-side uploads)
S3_ENPOINT=http://localhost:9000
After changing environment variables, restart the builder app (Ctrl+C then pnpm --filter builder dev) for the changes to take effect.

Step 6 — Configure your channel app

The exact steps differ per platform, but the pattern is the same.

Register the Webhook URL

Go to your channel’s developer console and add the webhook:
  • Callback URL / Webhook URL: https://abcd-1111.ngrok-free.app/integrations/<channel>/webhook
  • Verify Token: the value you set in Organization Settings for that channel
After saving, the platform will call GET <webhook-url> to verify. If the server responds correctly the webhook is active. Subscribe to the event types you need (e.g. messages, messaging_postbacks, message_reads for Messenger).

Register the OAuth Redirect URI

In your channel app’s Login / OAuth settings, add:
https://abcd-1111.ngrok-free.app/integrations/<channel>/callback

Configure Organization Settings in the app

Go to Organization Settings → [Channel Name] and fill in your App ID, App Secret, API version, and Verify Token. These values come from your channel developer console.

Step 7 — Verify the setup

Check webhook verification Most platforms send a GET request to the webhook URL when you save it. If the response is 200 OK with the challenge value, the webhook is verified. You can also test manually:
# Replace values with your tunnel URL, channel, and verify token
curl "https://abcd-1111.ngrok-free.app/integrations/messenger/webhook\
?hub.mode=subscribe\
&hub.verify_token=YOUR_VERIFY_TOKEN\
&hub.challenge=hello"
# Expected response: hello
Monitor live traffic ngrok’s web interface at http://127.0.0.1:4040 shows all requests and responses in real time — useful for debugging webhook payloads. Send a test message Send a message to your bot via the connected channel. You should see:
  • A POST /integrations/<channel>/webhook request in the ngrok dashboard
  • The message appear in the inbox inside the app

Troubleshooting

  • Make sure the verifyToken in Organization Settings exactly matches what you registered in the channel app
  • Confirm that the builder is running and the tunnel is active
  • Check the ngrok dashboard (http://127.0.0.1:4040) to see the raw request and response
  • Verify you have subscribed to the required event types in the channel app
  • Confirm the tunnel is still running (free ngrok tunnels time out after a period of inactivity on the free plan)
  • Check that NEXT_PUBLIC_BUILDER_URL and BETTER_AUTH_URL match the current tunnel URL exactly
  • Check that NEXT_PUBLIC_ASSET_URL in .env points to the RustFS tunnel URL (not localhost)
  • Make sure you restarted the builder after updating the env variable
  • Verify the RustFS tunnel is running: curl https://efgh-2222.ngrok-free.app/chatbotx/ should return a response from RustFS (even an error is fine — a connection refusal means the tunnel is down)
  • Ensure the Docker filesystem service is healthy: docker compose ps filesystem
This is expected on the free plan. After each restart:
  1. Note the new tunnel URLs from the ngrok terminal output
  2. Update BETTER_AUTH_URL, NEXT_PUBLIC_BUILDER_URL, and NEXT_PUBLIC_ASSET_URL in .env
  3. Restart the builder app
  4. Update the Webhook URL and OAuth Redirect URI in the channel developer console
To avoid repeating this, enable a ngrok static domain — one is included for free.

Alternative tunnel tools

If you prefer not to use ngrok, the following tools work the same way:
ToolCommandNotes
Cloudflare Tunnelcloudflared tunnel --url http://localhost:3123Free, random URL each run
localtunnelnpx localtunnel --port 3123Free, no account needed
Exposeexpose share http://localhost:3123Free and paid plans
Run a second instance of each on port 9000 for the RustFS tunnel. All subsequent configuration steps are identical — just substitute the tunnel URLs accordingly.