---
title: "WhatsApp setup"
description: "Meta app, access token, webhook verification."
section: "Setup"
canonical: https://wacrm.tech/docs/whatsapp-setup
dateModified: 2026-06-18
---
# WhatsApp setup

This template talks to the official **WhatsApp® Business Cloud API** (Meta). Each
account stores its own `phone_number_id`, `waba_id`, and encrypted
`access_token` — there is no shared app-level token. This page walks
through getting those values and wiring the webhook.

> **One WhatsApp number = one wacrm user.** Each `phone_number_id` can
> only be connected to a single user on a given instance, enforced both
> at the API layer (a second user gets a `409`) and by a DB constraint
> (`UNIQUE(phone_number_id)` since 0.2.1). RLS keeps each user's
> conversations private, so a number cannot be shared across logins
> even if you tried. If you need multiple humans to handle the same
> inbox, run them under a shared account.

## 1. Prerequisites

- A [Meta for Developers](https://developers.facebook.com) account.
- A Meta **Business Manager** account.
- A phone number that is **not** already in use on the regular WhatsApp or
  WhatsApp Business mobile apps. Landlines work; the number must be able
  to receive an SMS or voice call during verification.

## 2. Create the Meta app

1. In Meta for Developers, go to **My Apps → Create App**.
2. Pick the **Business** app type.
3. On the dashboard, under **Add products**, click **Set up** on
   **WhatsApp**.
4. Connect your Business Manager and accept the terms.

Meta gives you a **test number** for free. You can use it for local
testing, but for production you need to add your own number under
**WhatsApp → API Setup → From**.

## 3. Collect the IDs and the token

On **WhatsApp → API Setup** you will see:

| Value                 | Goes into                                        |
| --------------------- | ------------------------------------------------ |
| Phone number ID       | `phone_number_id` (entered in Settings → Connect) |
| WhatsApp Business ID  | `waba_id` (optional but recommended)              |
| Temporary access token | `access_token` (short-lived — see next step)      |

The temporary token expires after 24 hours. For production you must
generate a **System User** access token:

1. **Business Settings → Users → System users → Add**.
2. Name it `wacrm-system-user`, role **Admin**.
3. Click **Generate new token**, pick the app, and grant:
   - `whatsapp_business_management`
   - `whatsapp_business_messaging`
4. Copy the token. **This is shown only once.**

## 4. Connect it inside the app

1. Run the app locally (or open your deployed instance).
2. Sign in, then go to **Settings → WhatsApp**.
3. Fill in:
   - **Phone number ID** — from step 3.
   - **WhatsApp Business ID** — optional.
   - **Access token** — the System User token from step 3.
   - **Verify token** — any random string you make up; you will paste the
     same value into Meta in the next step.
4. Save. The app encrypts the access token and verify token with your
   `ENCRYPTION_KEY` before writing them to Supabase.

## 5. Configure the webhook in Meta

Under **WhatsApp → Configuration → Webhook**:

- **Callback URL**:
  - Local dev: run `npx ngrok http 3000` (or similar) and paste
    `https://<your-subdomain>.ngrok.app/api/whatsapp/webhook`.
  - Production: `https://<your-domain>/api/whatsapp/webhook`.
- **Verify token**: the same string you entered in step 4.
- Click **Verify and save**. Meta sends a GET with
  `hub.challenge` — the app's webhook route decrypts stored verify tokens
  and echoes the challenge back. If verification fails, double-check the
  verify token matches and the callback URL is publicly reachable.

### Subscribe to events

Under the webhook config, **Subscribe to** at minimum:

- `messages`
- `message_template_status_update`
- `message_template_quality_update`
- `message_template_components_update`

`messages` delivers inbound messages and delivery / read statuses. The
three `message_template_*` fields keep template approval status, quality
score, and Meta's auto-modifications in sync in real time — so a
template flips from Pending to Approved in the [Templates](https://wacrm.tech/docs/templates)
tab without a manual sync. If you only subscribe to the status field,
**Sync from Meta** is still the fallback for the other two.

## 6. Signature verification (required)

Set `META_APP_SECRET` to your app's **App Secret** (Meta for Developers →
App Settings → Basic). The app validates the `X-Hub-Signature-256` HMAC
on every inbound webhook and **rejects every request if the env var is
not set** — anyone who knew the webhook URL could otherwise spoof
messages and status updates. Configure it before pointing the webhook
at a deployed instance.

## 7. Send a test message

1. From Meta's **API Setup** page, send a test message to a number that is
   a registered tester.
2. Watch the Inbox — the message should appear within a second or two.
3. Reply from the app; the recipient gets a real WhatsApp message.

If nothing shows up, see [troubleshooting.md](https://wacrm.tech/docs/troubleshooting).

## Next step

[Environment variables →](https://wacrm.tech/docs/environment-variables)
