diff --git a/README.md b/README.md index e215bc4..06f5d10 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,43 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# WaCRM -## Getting Started +Self-hostable WhatsApp CRM — shared inbox, contacts, sales pipelines, +broadcasts, and no-code automations — built on Next.js 16 and Supabase. -First, run the development server: +- **Live demo / marketing site**: [`/`](./src/app/page.tsx) +- **Source**: + +## Quick start ```bash +git clone https://github.com//wacrm.git +cd wacrm +npm install +cp .env.local.example .env.local +# Fill in Supabase keys + ENCRYPTION_KEY, then: npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open . -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +For the full setup — Supabase migrations, WhatsApp Business API config, +production deploy on Hostinger — see [`docs/`](./docs/README.md). -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +## Documentation -## Learn More +- [Getting started](./docs/getting-started.md) +- [Supabase setup](./docs/supabase-setup.md) +- [WhatsApp setup](./docs/whatsapp-setup.md) +- [Environment variables](./docs/environment-variables.md) +- [Deploy on Hostinger](./docs/deployment-hostinger.md) +- [Automations cron](./docs/automations-and-cron.md) +- [Troubleshooting](./docs/troubleshooting.md) -To learn more about Next.js, take a look at the following resources: +## Stack -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +- **App** — Next.js 16 (App Router), React 19, TypeScript, Tailwind v4. +- **Data** — Supabase (Postgres + Auth + RLS). +- **WhatsApp** — Meta Cloud API (official WhatsApp Business API). -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +## License -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +MIT — fork it, brand it, host it. Pull requests welcome. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..36f1f16 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,43 @@ +# WaCRM documentation + +WaCRM is a self-hostable WhatsApp CRM built on Next.js and Supabase. These +docs walk you from a freshly forked repo to a production deploy. + +## Reading order + +1. **[Getting started](./getting-started.md)** — fork the repo, install, run + locally. +2. **[Supabase setup](./supabase-setup.md)** — create the database, run the + migrations, grab the keys. +3. **[WhatsApp setup](./whatsapp-setup.md)** — create a Meta app, connect a + phone number, wire the webhook. +4. **[Environment variables](./environment-variables.md)** — the full reference. +5. **[Deploy on Hostinger](./deployment-hostinger.md)** — recommended hosting + walkthrough (Node.js VPS). +6. **[Automations cron](./automations-and-cron.md)** — schedule the pending + executions drain so waits and delays fire. +7. **[Troubleshooting](./troubleshooting.md)** — the usual suspects. + +## What you will need before starting + +- A GitHub account (to fork the repo). +- A [Supabase](https://supabase.com) project on any paid or free tier. +- A [Meta for Developers](https://developers.facebook.com) account with a + WhatsApp Business app. +- A WhatsApp phone number that is **not** already tied to the regular + WhatsApp or WhatsApp Business mobile apps. +- A [Hostinger](https://www.hostinger.com/vps-hosting) VPS plan (or any Node + host that supports long-running Node.js 20+ processes). + +## Stack at a glance + +- **Frontend / API** — Next.js 16 (App Router), React 19, Tailwind v4. +- **Database + auth + storage** — Supabase (Postgres + RLS). +- **WhatsApp transport** — Meta Cloud API (official WhatsApp Business API). +- **Encryption** — AES-256-CBC for per-user access tokens. +- **Scheduler** — external cron that pings `GET /api/automations/cron`. + +## Getting help + +If something is unclear or broken, open an issue against the source repo: +. diff --git a/docs/automations-and-cron.md b/docs/automations-and-cron.md new file mode 100644 index 0000000..92c9b76 --- /dev/null +++ b/docs/automations-and-cron.md @@ -0,0 +1,85 @@ +# Automations cron + +WaCRM's **Automations** module lets you build flows that react to WhatsApp +events (new message, new contact, keyword match, schedule, etc.). Most +steps run inline — the exception is the **Wait** step, which parks the +execution in `automation_pending_executions` until its due time. + +A cron job has to drain that table. If you skip this, Wait steps never +resume and any flow that uses them stalls. + +## The endpoint + +``` +GET /api/automations/cron +Header: x-cron-secret: +``` + +- Returns `{ "processed": }` with how many rows were claimed. +- Returns `503` if the env var is not set. +- Returns `401` if the header is missing or wrong. + +The route claims up to 50 pending rows per call via a two-step +UPDATE-by-id so overlapping calls don't double-process. + +## 1. Generate the secret + +Any long random string works. One option: + +```bash +openssl rand -hex 32 +``` + +Copy the result into `AUTOMATION_CRON_SECRET` in your deployment env. + +## 2. Schedule the pinger + +### Option A — cron on the same VPS (Hostinger) + +```bash +crontab -e +``` + +```cron +* * * * * curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https://crm.example.com/api/automations/cron > /dev/null +``` + +Export the secret into the cron environment. The simplest way is to add +it to the user's shell profile and wrap the command: + +```cron +* * * * * bash -lc 'curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https://crm.example.com/api/automations/cron > /dev/null' +``` + +Or, for a more disciplined setup, use a `systemd` timer with +`EnvironmentFile=/etc/wacrm.env`. + +### Option B — external pinger + +Any uptime monitor that supports custom headers works: + +- [UptimeRobot](https://uptimerobot.com) — "Keyword Monitoring" with + custom HTTP headers. +- [Better Stack](https://betterstack.com). +- GitHub Actions with a `schedule:` trigger. + +Hit the URL once per minute. Anything slower just means Wait-step +resolution is that much more delayed. + +## 3. Verify + +```bash +curl -s -H "x-cron-secret: " https://crm.example.com/api/automations/cron +# -> {"processed":0} +``` + +Then create a test automation with a short Wait step (e.g., 30 seconds) +and trigger it. Watch the automation's **Logs** tab — the execution +should resume and mark as completed within one cron interval of the wait +expiring. + +## 4. What if you don't use Wait? + +Skip this page. Trigger → inline steps → done flows do not need a cron — +they finish synchronously inside the webhook handler or from the UI. The +cron only matters for flows that park execution. diff --git a/docs/deployment-hostinger.md b/docs/deployment-hostinger.md new file mode 100644 index 0000000..7785f2f --- /dev/null +++ b/docs/deployment-hostinger.md @@ -0,0 +1,161 @@ +# Deploy on Hostinger + +Hostinger's **VPS Hosting** plans are the recommended way to run WaCRM in +production: you get a dedicated Node.js process, predictable pricing, and +Hostinger's `hPanel` handles SSL and firewalls for you. + +This walkthrough uses a VPS running Ubuntu 24.04. + +## 1. Provision the VPS + +1. Sign up at and pick a plan. The + **KVM 2** tier (2 vCPU / 8 GB RAM) is a comfortable starting point. +2. During the setup wizard: + - **OS template** → Ubuntu 24.04 (plain, not the managed Node template — + we install what we need manually). + - **Location** → region closest to your users. + - **SSH key** → upload your public key (`~/.ssh/id_ed25519.pub` or + similar) so you can SSH in passwordless. +3. Wait for provisioning. In hPanel you will see the IPv4 address and + root credentials. + +## 2. SSH in and install dependencies + +```bash +ssh root@ + +# Create a non-root user for the app +adduser wacrm +usermod -aG sudo wacrm +rsync --archive --chown=wacrm:wacrm ~/.ssh /home/wacrm + +# Switch user +su - wacrm + +# Install Node.js 20 LTS +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt-get install -y nodejs git + +# Install PM2 for process management +sudo npm install --global pm2 +``` + +## 3. Clone your fork + +```bash +cd ~ +git clone https://github.com//wacrm.git +cd wacrm +npm ci +``` + +## 4. Configure env vars + +Create `/home/wacrm/wacrm/.env.local` with the values from +[environment-variables.md](./environment-variables.md). Make sure +`NEXT_PUBLIC_SITE_URL` is set to the exact public URL you will use +(e.g., `https://crm.example.com`). + +```bash +chmod 600 .env.local +``` + +## 5. Build and start with PM2 + +```bash +npm run build +pm2 start npm --name wacrm -- start +pm2 save +pm2 startup systemd -u wacrm --hp /home/wacrm +# Follow the command PM2 prints — it enables auto-start on reboot. +``` + +WaCRM is now listening on `127.0.0.1:3000`. + +## 6. Front with nginx + SSL + +Point your domain's `A` record at the VPS IP. Then: + +```bash +sudo apt-get install -y nginx certbot python3-certbot-nginx +``` + +Create `/etc/nginx/sites-available/wacrm`: + +```nginx +server { + server_name crm.example.com; + + client_max_body_size 25m; # WhatsApp media upload ceiling + + location / { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + listen 80; +} +``` + +Enable and issue a cert: + +```bash +sudo ln -s /etc/nginx/sites-available/wacrm /etc/nginx/sites-enabled/wacrm +sudo nginx -t && sudo systemctl reload nginx +sudo certbot --nginx -d crm.example.com +``` + +Certbot writes the 443 block, enables HTTP→HTTPS redirect, and schedules +renewals in `systemd`. + +## 7. Firewall + +hPanel → **Firewall**: allow inbound 22 (SSH), 80, 443. Everything else +stays closed. + +## 8. Update the Meta webhook + +Back in **Meta for Developers → WhatsApp → Configuration**, change the +callback URL to `https://crm.example.com/api/whatsapp/webhook` and re-verify. + +## 9. Schedule the automation cron + +Follow [automations-and-cron.md](./automations-and-cron.md) to drain +pending executions. A simple `cron` entry on the same VPS works: + +```bash +crontab -e +``` + +```cron +* * * * * curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https://crm.example.com/api/automations/cron > /dev/null +``` + +Load `AUTOMATION_CRON_SECRET` into the cron environment via +`/etc/environment` or an `EnvironmentFile=` in a `systemd` unit. + +## 10. Deploying updates + +```bash +ssh wacrm@ +cd ~/wacrm +git pull +npm ci +npm run build +pm2 reload wacrm +``` + +`pm2 reload` performs a zero-downtime restart. If the database schema +changed, apply any new SQL files from `supabase/migrations/` in the +Supabase SQL editor first — migrations are idempotent. + +## Where to go next + +- [Automations cron →](./automations-and-cron.md) — must-do if you use + the Wait step in any automation. +- [Troubleshooting →](./troubleshooting.md) — common deploy issues. diff --git a/docs/environment-variables.md b/docs/environment-variables.md new file mode 100644 index 0000000..00852ce --- /dev/null +++ b/docs/environment-variables.md @@ -0,0 +1,60 @@ +# Environment variables + +All runtime configuration lives in `.env.local` during development and in +your host's environment settings in production. `.env.local.example` is a +minimal template; the table below is the full reference. + +## Required + +| Variable | Where to find it | Notes | +| --------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------- | +| `NEXT_PUBLIC_SUPABASE_URL` | Supabase → Project Settings → API → **Project URL** | Public. Shipped to the browser. | +| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase → Project Settings → API → **anon / public** key | Public. Relies on RLS for safety. | +| `SUPABASE_SERVICE_ROLE_KEY` | Supabase → Project Settings → API → **service_role** key | **Secret.** Bypasses RLS. Used by webhook + admin routes only. | +| `ENCRYPTION_KEY` | Generate: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"` | 64 hex chars (32 bytes, AES-256-CBC). **Rotating breaks existing tokens.** | + +## Recommended + +| Variable | Purpose | +| ---------------------- | ---------------------------------------------------------------------------------------- | +| `META_APP_SECRET` | Meta → App Settings → Basic → **App Secret**. Enables `X-Hub-Signature-256` verification on incoming webhooks. | +| `NEXT_PUBLIC_SITE_URL` | Canonical public URL (e.g., `https://crm.example.com`). Used for absolute URLs, sitemap, OG images. | + +## Optional + +| Variable | Purpose | +| --------------------------- | -------------------------------------------------------------------------------------------------------- | +| `AUTOMATION_CRON_SECRET` | Shared secret that protects `GET /api/automations/cron`. Required if you schedule the automations drain. See [automations-and-cron.md](./automations-and-cron.md). | + +## Sample `.env.local` + +```bash +# Supabase +NEXT_PUBLIC_SUPABASE_URL=https://abcd1234.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi... +SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi... + +# Encryption — DO NOT change after first deploy +ENCRYPTION_KEY=3f9c0a7e4d8b2f1a6c5e8d4b9f0a2c6e8d4b9f0a2c6e8d4b9f0a2c6e8d4b9f0a + +# Public URL + Meta webhook signing +NEXT_PUBLIC_SITE_URL=https://crm.example.com +META_APP_SECRET=abcdef0123456789... + +# Automation cron +AUTOMATION_CRON_SECRET=generate-a-long-random-string +``` + +## Security checklist + +- Never commit `.env.local`. The repo already ignores it. +- On Hostinger / any VPS, set env vars via the platform's **Environment** + panel rather than writing them into a tracked file on disk. +- Rotate `SUPABASE_SERVICE_ROLE_KEY` if it leaks — Supabase lets you + regenerate it under Project Settings → API. +- Treat `ENCRYPTION_KEY` like a database master key. Losing it means + connected WhatsApp accounts must reconnect; rotating it means the same. + +## Next step + +[Deploy on Hostinger →](./deployment-hostinger.md) diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..ac83cfe --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,79 @@ +# Getting started + +Run WaCRM locally in about 15 minutes. This guide assumes Node.js 20+ and +npm are already installed. + +## 1. Fork and clone + +1. Go to and click **Fork** (top right). +2. Clone your fork: + + ```bash + git clone https://github.com//wacrm.git + cd wacrm + ``` + +## 2. Install dependencies + +```bash +npm install +``` + +## 3. Create the environment file + +Copy the template and fill in the values (full reference in +[environment-variables.md](./environment-variables.md)): + +```bash +cp .env.local.example .env.local +``` + +You cannot run `npm run dev` until **at least** `NEXT_PUBLIC_SUPABASE_URL` +and `NEXT_PUBLIC_SUPABASE_ANON_KEY` are set. You need the full Supabase +setup before WhatsApp features will work, so do that next. + +## 4. Set up Supabase + +Follow [supabase-setup.md](./supabase-setup.md) to create the project and +run the migrations. Come back here when the URL, anon key, and service-role +key are populated in `.env.local`. + +## 5. Generate the encryption key + +Access tokens for WhatsApp are encrypted at rest with AES-256-CBC. Generate +a 64-character hex key: + +```bash +node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" +``` + +Paste the output into `ENCRYPTION_KEY` in `.env.local`. **Do not change +this value later** — existing tokens are encrypted with whatever value was +set at the time, and rotating it will make them unreadable. + +## 6. Run the dev server + +```bash +npm run dev +``` + +Open . You should see the marketing landing page. + +- Create an account at `/signup`. +- After sign-in you land on `/dashboard`. +- The inbox, contacts, pipelines, automations, and broadcasts modules are + all empty until you connect a WhatsApp number + ([whatsapp-setup.md](./whatsapp-setup.md)). + +## 7. Commands cheat sheet + +| Command | Purpose | +| ---------------- | ---------------------------------------------- | +| `npm run dev` | Dev server with Turbopack HMR on `:3000`. | +| `npm run build` | Production build. | +| `npm start` | Run the production build. | +| `npm run lint` | ESLint across the project. | + +## Next step + +[Supabase setup →](./supabase-setup.md) diff --git a/docs/supabase-setup.md b/docs/supabase-setup.md new file mode 100644 index 0000000..85bbd73 --- /dev/null +++ b/docs/supabase-setup.md @@ -0,0 +1,84 @@ +# Supabase setup + +WaCRM uses Supabase for Postgres, authentication, row-level security (RLS), +and (optionally) storage. You need one Supabase project per WaCRM +deployment. + +## 1. Create the project + +1. Sign in at and create a **new project**. +2. Pick the region closest to your users. Save the database password shown + at creation time — you will not see it again. +3. Wait for the project to provision (about a minute). + +## 2. Grab your keys + +Open **Project Settings → API** in the Supabase dashboard. Copy: + +- **Project URL** → `NEXT_PUBLIC_SUPABASE_URL` +- **anon / public** key → `NEXT_PUBLIC_SUPABASE_ANON_KEY` +- **service_role** key → `SUPABASE_SERVICE_ROLE_KEY` + +Paste them into `.env.local`. + +> **Do not commit the service-role key.** It bypasses RLS entirely. The +> repo's `.gitignore` already excludes `.env.local` — keep it that way. + +## 3. Run the migrations + +All schema changes live in `supabase/migrations/` as plain SQL and are +idempotent (safe to re-run). The simplest way to apply them: + +### Option A — SQL Editor (quickest) + +1. Open **SQL Editor** in the Supabase dashboard. +2. For each file in `supabase/migrations/`, in numeric order: + - `001_initial_schema.sql` + - `002_pipelines_enhancements.sql` + - `003_broadcast_recipient_wamid.sql` + - `004_contact_delete_set_null.sql` + - `005_broadcast_counts_incremental.sql` + - `006_automations.sql` + - `007_automations_increment_counter.sql` +3. Paste into the editor and run. Each file prints its own status. + +### Option B — Supabase CLI + +```bash +npm install --global supabase +supabase login +supabase link --project-ref +supabase db push +``` + +The CLI applies every file in `supabase/migrations/` on top of the linked +project. + +## 4. Verify + +Open **Table Editor** in the Supabase dashboard. You should see tables +including `profiles`, `contacts`, `conversations`, `messages`, `pipelines`, +`broadcasts`, `automations`, and `whatsapp_config`. If any are missing, a +migration failed — re-run it and check the SQL output. + +## 5. Auth settings + +Under **Authentication → Providers**, confirm: + +- **Email** is enabled (default). +- **Confirm email** is on for production, off for local if you want + frictionless testing. + +Under **Authentication → URL Configuration**, add your production URL +(e.g., `https://crm.example.com`) to the allow-list so password-reset +emails link back correctly. + +## 6. (Optional) Storage + +WaCRM downloads WhatsApp media through Meta's `/download` endpoint and +currently relays it on demand rather than caching it in Supabase Storage. +No bucket setup is required for a default install. + +## Next step + +[WhatsApp setup →](./whatsapp-setup.md) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..586c534 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,118 @@ +# Troubleshooting + +The greatest hits. If your problem is not here, open an issue at +. + +## Build-time + +### `Error: ENCRYPTION_KEY must be 64 hex chars` + +`ENCRYPTION_KEY` is missing or not the right length. Generate one: + +```bash +node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" +``` + +Put the output into `.env.local` / your production env. + +### `Error: supabaseUrl is required` + +`NEXT_PUBLIC_SUPABASE_URL` or `NEXT_PUBLIC_SUPABASE_ANON_KEY` is not set +at build time. On Hostinger / any Node host, make sure the env vars are +exported **before** `npm run build` runs. + +### Next.js complains about missing framework docs + +The repo's `AGENTS.md` pins contributors to the docs bundled in +`node_modules/next/dist/docs/` — that notice is for humans editing the +code, it does not affect your build. + +## Auth + +### Sign-up succeeds but the confirmation email never arrives + +In Supabase → **Authentication → Providers → Email**, either: + +- Configure a custom SMTP provider (recommended for production), or +- Temporarily turn off **Confirm email** while testing. + +### Password-reset links point at `localhost:3000` in production + +Set `NEXT_PUBLIC_SITE_URL` **and** add the same origin to Supabase → +**Authentication → URL Configuration → Redirect URLs**. + +## WhatsApp + +### Webhook verification fails in Meta + +- The **verify token** in Meta must match exactly what you saved in + **Settings → WhatsApp** inside WaCRM. +- The callback URL must be reachable without auth. Test it yourself: + `curl 'https://crm.example.com/api/whatsapp/webhook?hub.mode=subscribe&hub.verify_token=&hub.challenge=test'` + should return `test`. +- If you set `META_APP_SECRET`, check the app secret matches — a bad + secret causes 401s on inbound webhooks, but verification itself does + not use it. + +### Messages go out but nothing comes in + +1. Confirm the webhook is **Subscribed** to `messages` under Meta → + WhatsApp → Configuration. +2. Confirm the phone number is listed under **Recipients** during testing + (test-number apps only deliver to whitelisted testers). +3. Check server logs — inbound webhooks hit + `POST /api/whatsapp/webhook`. A 200 with no message row means the + payload parsed successfully but didn't match any known contact's + config — double-check `phone_number_id`. + +### `Token decryption failed` + +`ENCRYPTION_KEY` changed since the token was saved. Reconnect the +WhatsApp account from **Settings → WhatsApp** to re-encrypt with the +current key. + +## Automations + +### Wait steps never resume + +The cron drain is not running. See +[automations-and-cron.md](./automations-and-cron.md). + +Quick sanity check from your machine: + +```bash +curl -s -H "x-cron-secret: " https://crm.example.com/api/automations/cron +``` + +If that returns `{"processed":N}` with N growing when a wait is due, the +endpoint is fine and the problem is just the schedule. + +### An automation fires twice for the same message + +Most likely two WaCRM instances share one Supabase project and both +receive the same webhook. The webhook handler deduplicates by Meta's +`wamid`, but only within a single deploy — split-brain setups can double +up. Either consolidate to one deploy or route the webhook to exactly one. + +## Deploy + +### `502 Bad Gateway` from nginx + +- `pm2 status` — is the `wacrm` process up? +- `pm2 logs wacrm` — is the Node process crashing on boot (usually a + missing env var)? +- `sudo nginx -t` — is the nginx config valid? + +### Meta webhook works until the cert renews + +Certbot renews in-place and reloads nginx. If you see a certificate error +right after renewal, restart nginx (`sudo systemctl restart nginx`) and +check `/var/log/letsencrypt/letsencrypt.log` for the last renew attempt. + +## Still stuck? + +Open an issue with: + +- Environment (local / Hostinger / other). +- The exact error from server logs (`pm2 logs wacrm` or Next's dev output). +- What you were trying to do when it happened. diff --git a/docs/whatsapp-setup.md b/docs/whatsapp-setup.md new file mode 100644 index 0000000..7812356 --- /dev/null +++ b/docs/whatsapp-setup.md @@ -0,0 +1,104 @@ +# WhatsApp setup + +WaCRM 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. + +## 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 WaCRM + +1. Run WaCRM 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. WaCRM 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://.ngrok.app/api/whatsapp/webhook`. + - Production: `https:///api/whatsapp/webhook`. +- **Verify token**: the same string you entered in step 4. +- Click **Verify and save**. Meta sends a GET with + `hub.challenge` — WaCRM'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` + +`messages` delivers inbound messages and delivery / read statuses; +`message_template_status_update` keeps Meta-approved template statuses in +sync on the broadcast UI. + +## 6. Signature verification (recommended) + +Set `META_APP_SECRET` to your app's **App Secret** (Meta for Developers → +App Settings → Basic). When present, WaCRM validates the `X-Hub-Signature-256` +header on every inbound webhook and drops unsigned or forged calls. + +## 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 WaCRM Inbox — the message should appear within a second or + two. +3. Reply from WaCRM; the recipient gets a real WhatsApp message. + +If nothing shows up, see [troubleshooting.md](./troubleshooting.md). + +## Next step + +[Environment variables →](./environment-variables.md)