WhatsApp Cloud — Manual Connection with a System User¶
How to connect a WhatsApp Cloud (Meta) number to ConnectGain manually, using a Meta System User token instead of the Facebook login wizard.
Use this guide when the number you want does not appear in the "Connect with Facebook" selector / Embedded Signup flow.
1. When to use this (vs. the Facebook wizard)¶
ConnectGain has two ways to connect a WhatsApp Cloud number, and they use two different Meta identities:
| Flow | Identity used | Sees only… |
|---|---|---|
| Connect with Facebook (wizard / Embedded Signup) | the person who logs in | WABAs that person administers and grants on the consent screen |
| Manual Entry (this guide) | a System User token you paste | WABAs assigned to that system user |
A number is missing from the wizard whenever the human logging in is not an admin of the WABA's owning business — most commonly a partner/client WABA that was assigned to a system user for server-to-server use, but never assigned to any person. The wizard can only show what the logged-in human can see; the system user, on the other hand, has the WABA assigned, so Manual Entry is the path.
Rule of thumb: if the WABA was set up for API/server use, it's a system-user WABA → connect it manually.
2. The three pieces you need¶
A WhatsApp Cloud connection is not one object — it's three:
| Piece | What it is | Where to get it |
|---|---|---|
| Access Token | the System User token (the actor that calls Meta) | Business Settings → Users → System Users → Generate token |
| Phone Number ID | the number under the WABA (not the phone number itself) | App Dashboard → WhatsApp → API Setup |
| WABA ID | the WhatsApp Business Account asset | App Dashboard → WhatsApp → API Setup ("WhatsApp Business Account ID") |
You authenticate as the system user, but you connect to a WABA. The system-user ID never goes into the form — the WABA ID and Phone Number ID do.
A
100059…-style ID is a user / system-user profile, not a WABA. WABA IDs look like995632549547163. Don't paste a system-user ID into the WABA field.
3. Prerequisites in Meta Business Manager¶
Do these once, in business.facebook.com → Business Settings of the business that owns the WABA:
- Assign the WABA to the system user
- Users → System Users → select (or create) the system user.
- Add Assets → WhatsApp Accounts → select the WABA → enable Manage / Full control → Save.
-
If there are multiple system users with the same name, match by ID, not name.
-
Generate a permanent token
- On that system user, Generate New Token → select the ConnectGain/Appgain app → Token expiration: Never.
- Tick scopes:
whatsapp_business_managementandwhatsapp_business_messaging. - Copy the token — this is your Access Token. (A system-user token never expires, which is why it's preferred over a short-lived login token.)
4. Verify access before you connect (recommended)¶
Confirm the token can actually reach the number before wiring it in. Replace
<TOKEN>, <PHONE_NUMBER_ID>, <WABA_ID> (Graph API v22.0):
# (a) The token acts AS the right system user
curl "https://graph.facebook.com/v22.0/me?fields=id,name&access_token=<TOKEN>"
# (b) The number is reachable and live → expect status: CONNECTED
curl "https://graph.facebook.com/v22.0/<PHONE_NUMBER_ID>?fields=display_phone_number,verified_name,status,account_mode&access_token=<TOKEN>"
# (c) The WABA is approved & the business is verified
curl "https://graph.facebook.com/v22.0/<WABA_ID>?fields=name,account_review_status,business_verification_status&access_token=<TOKEN>"
If (b) returns Object … does not exist, cannot be loaded due to missing
permissions (code 100 / subcode 33), the WABA is not assigned to this system
user — go back to step 3.1.
5. Connect in ConnectGain¶
- Go to Settings → Channels and click Add Channel (opens the "Add New Channel" page).
- Select WhatsApp Cloud.
- Choose connection mode Manual Entry, then provider Meta.
- Fill in:
| Field | Value |
|---|---|
| Access Token | the system user token from step 3.2 |
| Phone Number ID | from App Dashboard → WhatsApp → API Setup |
| WABA ID | from App Dashboard → WhatsApp → API Setup |
| Verify Token | any string you invent (e.g. cg_<name>_<random>) — see step 6 |
| Confirm Account Password | your ConnectGain login password (used once to provision channel credentials) |
- Save.
What happens on save (identical to the Facebook wizard's Appgain calls):
- Provisions a new Appgain suit → { suitId, apiKey }.
- Configures the Meta vendor on the suit:
PUT https://notify.appgain.io/{suitId}/config with
vendor: "meta" and connection_info: { type, phone_number_id, waba_id, access_token }.
- Inserts the channel (with waba_id persisted).
- Syncs the same token to Appgain as the FBSessionToken so Appgain can act on
behalf of the number.
The Access Token is also used as the Facebook session token — they are the same value, so there is no separate "Facebook Session Token" field.
6. Configure the webhook on Meta (so inbound + delivery status work)¶
In App Dashboard → WhatsApp → Configuration → Webhook: - Verify token: the exact same string you typed in the form. - Callback URL: your ConnectGain WhatsApp Cloud webhook endpoint. - Subscribe to the messages field.
The Verify Token is not something you fetch from Meta — it's a shared secret you choose, entered in both places so Meta's verification handshake matches.
Without the webhook, messages can still be sent, but you will not receive inbound messages or delivery/read/failed status callbacks.
7. Send a test message¶
WhatsApp requires an approved template to open a conversation (outside the customer's 24-hour service window). Supply a value for every template variable:
curl -X POST "https://graph.facebook.com/v22.0/<PHONE_NUMBER_ID>/messages" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "<RECIPIENT_E164_NO_PLUS>",
"type": "template",
"template": {
"name": "<template_name>",
"language": { "code": "<lang_code>" },
"components": [
{ "type": "body", "parameters": [
{ "type": "text", "text": "Value1" },
{ "type": "text", "text": "Value2" }
] }
]
}
}'
A messages[].message_status: "accepted" response means Meta queued the message.
Final delivered / read / failed status arrives only via the webhook (step 6).
To find a template's exact language and how many variables it expects:
curl "https://graph.facebook.com/v22.0/<WABA_ID>/message_templates?name=<template_name>&fields=name,status,category,language,components&access_token=<TOKEN>"
8. Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
| Number not listed in the Facebook wizard | The WABA is assigned to a system user, not the human logging in | Use Manual Entry (this guide), or assign the WABA to your person in Business Settings |
code 100 / subcode 33 "does not exist / missing permissions" on the WABA or number |
Token's identity has no access to that WABA | Assign the WABA to the system user (step 3.1) |
Token's /me returns the wrong system user |
Two system users (often same name) — token generated from the wrong one | Generate the token from the system user that has the WABA assigned; match by ID |
(#100) nonexisting field (phone_numbers) on an ID |
That ID is not a WABA (e.g. a Page or system-user profile) | Use the real WABA ID from API Setup |
(#132000) Number of parameters does not match |
Template has body variables you didn't fill | Add a body component with one parameters entry per {{n}} |
Send is accepted but not delivered |
No webhook capturing status; or the number's display name is PENDING_REVIEW (can_send_message: LIMITED); or recipient undeliverable (131026) |
Configure the webhook (step 6) to see the real status; wait for display-name approval to raise limits; confirm the recipient is on WhatsApp |
Related Documentation¶
Last updated: 2026-06-08.