This guide walks through every step needed to connect a WhatsApp Business phone number to Open Chat Studio (OCS) using the Meta Cloud API. It covers Meta app creation, credential gathering, OCS configuration, infrastructure requirements, and common pitfalls — all based on real experience setting this up from scratch.
Prerequisites
- A Meta Business account at business.facebook.com
- A phone number dedicated to the chatbot — this number cannot simultaneously be used on the WhatsApp mobile app
- An Open Chat Studio instance accessible over HTTPS with a valid SSL certificate (a domain name is required — bare IP addresses won’t work)
- Celery worker and Redis running on the OCS instance
Overview
The setup involves three areas:
- Meta Developer Dashboard — create an app, gather credentials, configure the webhook
- Open Chat Studio — create a messaging provider and WhatsApp channel
- Infrastructure — ensure HTTPS, correct Django Site domain, and background workers
Part 1: Meta Configuration
Step 1: Create a Meta App
- Go to developers.facebook.com/apps
- Click Create App
- Select the Business type
- Select your Business portfolio
- Name the app and click Create
Step 2: Add WhatsApp Product
- In your app dashboard, click Add Product (or go to Use cases)
- Find WhatsApp / “Connect with customers through WhatsApp” and set it up
- WhatsApp configuration options should now appear under Use cases → Customize in the left sidebar
Step 3: Add Your Phone Number
- Go to WhatsApp → API Setup
- Click Add phone number
- Enter your dedicated phone number and verify it via SMS or voice call
Important: If the number is currently registered on the WhatsApp mobile app, you must first delete the WhatsApp account from your phone (Settings → Account → Delete my account), wait a few minutes, then register it on the Cloud API. A number cannot be on both the mobile app and Cloud API simultaneously.
If you get an error about “Upgrading from a consumer app”, delete the WhatsApp account from the phone, wait 5–30 minutes, then register via the API:
curl -X POST "https://graph.facebook.com/v22.0/PHONE_NUMBER_ID/register" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"messaging_product": "whatsapp", "pin": "123456"}'
Step 4: Gather Credentials
You need four values from Meta:
4a. WhatsApp Business Account ID
- Go to Meta Business Suite → Settings → Business Settings → Accounts → WhatsApp Accounts
- Select your account — the ID is displayed in the info panel
- Alternatively, it’s shown on the WhatsApp → API Setup page in the developer dashboard
4b. App Secret
- Go to developers.facebook.com/apps → your app → App Settings → Basic
- Click Show next to App Secret and copy it
4c. System User Access Token (permanent)
The temporary token from the API Setup page expires in 24 hours. Create a permanent one:
- Go to Meta Business Suite → Settings → Business Settings → Users → System Users
- Click Add to create a system user with Admin role (or select an existing one)
- Click the system user → Add Assets:
- Add your App with Full Control
- Add your WhatsApp Account with Full Control
- Click Generate New Token:
- Select your app
- Select permissions:
whatsapp_business_managementandwhatsapp_business_messaging - Set expiration to Never
- Click Generate Token and copy it immediately — it won’t be shown again
4d. Verify Token
This is a secret string you create yourself (e.g., my-ocs-webhook-2024-secure). You’ll enter the same value in both Meta’s webhook config and OCS.
Step 5: Subscribe the App to the WhatsApp Business Account
This step is critical and easy to miss. Without it, Meta will not deliver webhooks even if everything else is configured correctly.
curl -X POST "https://graph.facebook.com/v22.0/WHATSAPP_BUSINESS_ACCOUNT_ID/subscribed_apps" \
-H "Authorization: Bearer ACCESS_TOKEN"
Verify the subscription:
curl -s "https://graph.facebook.com/v22.0/WHATSAPP_BUSINESS_ACCOUNT_ID/subscribed_apps" \
-H "Authorization: Bearer ACCESS_TOKEN"
The response should include your app in the data array.
Step 6: Publish the App
An unpublished app only receives test webhooks from the dashboard, not real messages.
- Go to your app Dashboard → Publish (left sidebar)
- Complete the requirements:
- Privacy Policy URL — enter a URL in App Settings → Basic
- Use case review — complete any listed requirements
- Click Publish
Part 2: Open Chat Studio Configuration
Step 1: Configure the Django Site Domain
OCS uses Django’s Sites framework to generate webhook URLs. The default is localhost:8000, which won’t work in production.
python manage.py shell -c "
from django.contrib.sites.models import Site
site = Site.objects.get(id=1)
site.domain = 'your-domain.com'
site.name = 'Open Chat Studio'
site.save()
print(f'Updated: {site.domain}')
"
Also set HTTPS in your environment:
USE_HTTPS_IN_ABSOLUTE_URLS=True
Restart the app after these changes.
Step 2: Create a Messaging Provider
- Log in to OCS → navigate to your team
- Go to Settings → Service Providers
- Click Add and select type: Meta Cloud API (WhatsApp)
- Fill in:
| Field | Value |
|---|---|
| Name | Descriptive name (e.g., “My WhatsApp Business”) |
| Business ID | WhatsApp Business Account ID (Part 1, Step 4a) |
| Access Token | System User permanent token (Part 1, Step 4c) |
| App Secret | App Secret (Part 1, Step 4b) |
| Verify Token | The secret string you created (Part 1, Step 4d) |
- Save
Step 3: Create a WhatsApp Channel
- Navigate to your Chatbot (Experiment)
- Go to the Channels tab → Add Channel
- Configure:
- Platform: WhatsApp
- Messaging Provider: Select the provider from Step 2
- Phone Number: Your WhatsApp Business phone number (e.g.,
+255 791 807 896)
- Save
OCS validates the phone number against the Meta Business Account and resolves the internal phone_number_id. After saving, the webhook URL is displayed:
https://your-domain.com/channels/whatsapp/meta/incoming_message
Part 3: Configure the Webhook in Meta
Step 1: Verify the Webhook
- Go to developers.facebook.com/apps → your app
- Navigate to WhatsApp → Configuration (under Use cases → Customize → left sidebar)
- Under Webhook, click Edit
- Enter:
- Callback URL:
https://your-domain.com/channels/whatsapp/meta/incoming_message - Verify Token: the exact string you entered in OCS
- Callback URL:
- Click Verify and Save
Tip: Test verification locally first:
curl -s "https://your-domain.com/channels/whatsapp/meta/incoming_message?hub.mode=subscribe&hub.verify_token=YOUR_TOKEN&hub.challenge=test123"This should return
test123. If it returnsVerification failed., the token doesn’t match what’s stored in OCS.
Step 2: Subscribe to Webhook Fields
- On the same Configuration page, under Webhook fields, click Manage
- Subscribe to the
messagesfield - Save
Part 4: Infrastructure Requirements
HTTPS with a Valid Certificate
Meta requires HTTPS with a valid SSL certificate. A bare IP address won’t work — you need a domain name.
Using Caddy as a reverse proxy (recommended):
Install Caddy, then configure /etc/caddy/Caddyfile:
your-domain.com {
reverse_proxy localhost:8000
}
Restart Caddy: sudo systemctl restart caddy. Caddy automatically provisions and renews Let’s Encrypt certificates.
Django Settings
ALLOWED_HOSTS=your-domain.com
CSRF_TRUSTED_ORIGINS=https://your-domain.com
Background Workers
The webhook handler dispatches messages to Celery for async processing. Both Celery and Redis must be running:
# Check Redis
sudo systemctl status redis
# Start Celery worker
celery -A config worker -l info
Part 5: Verification and Testing
- From a phone, send a WhatsApp message to your business number
- Monitor logs at each stage:
# Caddy — is Meta hitting the webhook?
sudo journalctl -u caddy --since "2 minutes ago" -f
# Django — any errors?
sudo journalctl -u your-ocs-service --since "2 minutes ago"
# Celery — is the message being processed?
sudo journalctl -u your-celery-service --since "2 minutes ago"
- You should receive a response from the chatbot
You can also verify your phone number status via the API:
curl -s "https://graph.facebook.com/v22.0/PHONE_NUMBER_ID\
?fields=display_phone_number,verified_name,code_verification_status,platform_type" \
-H "Authorization: Bearer ACCESS_TOKEN" | python3 -m json.tool
Expected: platform_type: "CLOUD_API" and code_verification_status: "VERIFIED".
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Webhook verification fails in Meta | Verify token mismatch | Re-enter the verify token in OCS messaging provider, then retry |
| Webhook verified but no messages arrive | App not subscribed to WABA | Run POST /{WABA_ID}/subscribed_apps (Part 1, Step 5) |
| Webhook verified but no messages arrive | App not published | Publish the app (Part 1, Step 6) |
| Messages arrive but 500 error in Django | Celery task not registered | Restart the Celery worker |
| Messages arrive but no response | Celery worker not running | Start Celery and verify Redis is running |
Verification failed. on curl test |
Token hash mismatch in DB | Edit OCS messaging provider and re-save with correct verify token |
| Phone number validation fails in OCS | Wrong Business ID | Ensure Business ID matches the WABA that owns the phone number |
Upgrading from consumer app not allowed |
Number still on WhatsApp mobile app | Delete WhatsApp account from phone, wait 5–30 min, register via API |
localhost:8000 in webhook URL |
Django Site domain not updated | Update Site.objects.get(id=1).domain and set USE_HTTPS_IN_ABSOLUTE_URLS=True |
| Meta sends webhooks but connection drops | Django app crashing | Check Django logs for the traceback |
How It All Fits Together
User Phone
→ WhatsApp
→ Meta Cloud API
→ POST /channels/whatsapp/meta/incoming_message
→ Verify X-Hub-Signature-256 (app_secret)
→ Route by phone_number_id → ExperimentChannel
→ Celery task → LLM processing
→ POST /{phone_number_id}/messages (via access_token)
→ Meta Cloud API → WhatsApp → User Phone
All Meta Cloud API channels share a single webhook endpoint. Routing to the correct chatbot happens by matching the phone_number_id from the incoming payload against stored channel data in OCS.