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.

  1. App ID META_APP_ID

    Meta app that owns the webhook.

  2. App Secret META_APP_SECRET

    Exchanges the code. Callers cannot send it.

  3. Configuration ID META_CONFIGURATION_ID

    Embedded Signup config_id for FB.login.

  4. API version META_API_VERSION

    Graph version. Default v24.0.

POST /v1/onboarding/connect

  • code Embedded Signup
  • wabaId WABA id
  • phoneNumberId Phone number id
  • pin 6-digit PIN

02

Two steps

  1. Connect the line

    Send the code, the WABA id, the phone number id, and the PIN. The gateway finishes the line.

  2. Send

    Name that phone number id. Watahub uses the stored token. You do not pass it.

connect.sh $GATEWAY is the host you deploy. This domain is the project page.

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