Open-source WhatsApp Cloud API gateway
Paste four Meta values. Watahub keeps the business token.
One operator. MIT. Cloudflare Workers and Node.
01
How you connect
Four Meta values on the gateway. Once.
-
App ID
META_APP_IDMeta app that owns the webhook.
-
App Secret
META_APP_SECRETExchanges the code. Callers cannot send it.
-
Configuration ID
META_CONFIGURATION_IDEmbedded Signup config_id for FB.login.
-
API version
META_API_VERSIONGraph version. Default v24.0.
POST /v1/onboarding/connect
codeEmbedded SignupwabaIdWABA idphoneNumberIdPhone number idpin6-digit PIN
02
Two steps
-
Connect the line
Send the code, the WABA id, the phone number id, and the PIN. The gateway finishes the line.
-
Send
Name that phone number id. Watahub uses the stored token. You do not pass it.
01 POST /v1/onboarding/connect
curl -X POST "$GATEWAY/v1/onboarding/connect" \
-H "Authorization: Bearer $WATAHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "AQD...embedded-signup-code",
"wabaId": "105555123456789",
"phoneNumberId": "100609346426000",
"pin": "581063"
}'
200 no accessToken
{
"phoneNumberId": "100609346426000",
"wabaId": "105555123456789",
"subscribed": true,
"registered": true
}
02 POST /v1/messages
curl -X POST "$GATEWAY/v1/messages" \
-H "Authorization: Bearer $WATAHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+15551234567",
"phoneNumberId": "100609346426000",
"kind": "text",
"text": { "body": "Hello from Watahub" }
}'
03
What you never touch
-
System user token
Connect stores a business token for that phone number id.
-
Manual WABA subscribe
Connect subscribes the WABA to messages.
-
Manual phone register
Connect registers the phone with the 6-digit PIN.
04
Self-host
-
Cloudflare Workers
The gateway is its own Worker. This page is not that Worker.
-
Node
Same routes.
pnpm run dev:node. -
One subscriber webhook
Meta posts to
/webhook. You get one signed, normalized delivery.
After connect
The access token is not in the response.
{
"phoneNumberId": "100609346426000",
"wabaId": "105555123456789",
"subscribed": true,
"registered": true
}
The next send uses the token Watahub stored.
Source on GitHub