/opt/mawid
NameSizeModeActions
.git/-0755rm
apps/-0755rm
backups/-0755rm
packages/-0755rm
scripts/-0755rm
.env.droplet7840644editdlrm
.gitignore990644editdlrm
.nvmrc80644editdlrm
.prettierignore520644editdlrm
.prettierrc730644editdlrm
Caddyfile2420644editdlrm
DEMO.md53980644editdlrm
deploy.sh7890755editdlrm
docker-compose.droplet.yml10570644editdlrm
docker-compose.prod.yml10330644editdlrm
docker-compose.yml21870644editdlrm
eslint.config.mjs6470644editdlrm
GOLIVE.md83250644editdlrm
package.json6190644editdlrm
pnpm-lock.yaml2477460644editdlrm
pnpm-workspace.yaml1850644editdlrm
PROGRESS.md225590644editdlrm
PROJECT_PLAN.md129970644editdlrm
README.md9820644editdlrm
RUNBOOK.md51250644editdlrm
tsconfig.base.json3160644editdlrm
Edit: /opt/mawid/GOLIVE.md (8325B)
# Go-Live Setup Guide The three external prerequisites for the pilot, in the order to do them. When all three are done, follow RUNBOOK §1 for the deploy and RUNBOOK §2 to onboard the first clinic. --- ## 1. VPS + domains (do this first — the Meta webhook needs a public HTTPS URL) ### 1.1 Server - Any provider works; plan target is a DigitalOcean droplet. Minimum: 2 vCPU / 4 GB RAM / 50 GB disk, Ubuntu 24.04 LTS. - Install Docker Engine + Compose plugin: ```bash curl -fsSL https://get.docker.com | sh ``` - Basic hygiene: create a non-root user in the `docker` group, disable SSH password auth, enable ufw allowing only 22/80/443. - **Disk monitoring matters**: Docker build cache grows on every deploy. Add a weekly cron: `docker builder prune -af --filter until=168h`. ### 1.2 Domains - Pick two hostnames, e.g. `api.yourdomain.com` and `app.yourdomain.com`. - Create DNS **A records** for both pointing at the droplet's IP. Wait for propagation (`dig api.yourdomain.com`). - Caddy obtains and renews TLS certificates automatically on first request — no certbot needed. Ports 80 **and** 443 must be open. ### 1.3 First deploy ```bash git clone /opt/mawid && cd /opt/mawid cp .env.example .env.production chmod +x deploy.sh scripts/*.sh ``` Fill `.env.production`: | Var | Value | |---|---| | `DATABASE_URL` | `postgresql://mawid:@postgres:5432/mawid` (also change `POSTGRES_PASSWORD` in docker-compose.yml or use env there) | | `REDIS_URL` | `redis://redis:6379` | | `JWT_SECRET` | `openssl rand -hex 32` | | `API_DOMAIN` / `DASHBOARD_DOMAIN` | bare hostnames from 1.2 | | `NODE_ENV` | `production` | | `OPENAI_API_KEY`, `WA_*` | from sections 2 and 3 below (deploy will refuse to boot in production without them — for a first infra-only test, deploy as `NODE_ENV=staging`… no: use `development` temporarily, then flip) | Then `./deploy.sh .env.production` and check `https://api.yourdomain.com/health`. Set up the backup cron (RUNBOOK §4) the same day. --- ## 2. OpenAI API key + agent smoke test ### 2.1 Key 1. Create an org account at platform.openai.com (not a personal free tier — you want billing + usage limits). 2. Billing → add payment method → set a **monthly budget cap** (start: $50) and an email alert threshold. 3. API Keys → Create new secret key, scoped to a project (create project "mawid-pilot"). Copy once — it is shown only once. 4. Put it in `.env.production` as `OPENAI_API_KEY`, redeploy. Models used (already configured in code): `gpt-5.6-luna` (intent gate), `gpt-5.6-terra` (booking agent). Rough pilot cost: a booking conversation is ~5–10 calls ≈ $0.01–0.03; a 200-conversation month ≪ $10. ### 2.2 Live smoke test (required — mocked tests can't catch API-side changes) With the stack running and the key set: 1. Temporarily set `FEATURE_ECHO_MODE=false` (it overrides the agent). 2. Send a WhatsApp text from a test phone: *"Merhaba, yarın için muayene randevusu almak istiyorum."* 3. Expect: agent replies in Turkish offering real slots (check `docker compose logs -f api` — you should see tool calls audited: `tool:get_services`, `tool:get_availability`). 4. Book, then reschedule, then cancel — all via chat. Verify in the dashboard calendar after each step. 5. Repeat one booking in Arabic: *"مرحبا، بدي موعد تنظيف أسنان بكرا"*. 6. If calls fail with a 400 naming `reasoning_effort` or the model id, check PROGRESS.md "agent LLM" notes — model ids live in `apps/api/src/agent/openai.client.ts`. --- ## 3. Meta WhatsApp Business Cloud API Prereqs: a Facebook account, a verified **Meta Business Portfolio** (business.facebook.com — verification can take days, start early), and a phone number for the clinic **not currently registered on consumer WhatsApp**. ### 3.1 App + WhatsApp product 1. developers.facebook.com → My Apps → **Create App** → type **Business** → link it to your Business Portfolio. 2. In the app dashboard, **Add product → WhatsApp → Set up**. This creates a WhatsApp Business Account (WABA) with a free test number. 3. Note from *WhatsApp → API Setup*: - **Phone number ID** → goes to `WA_PHONE_NUMBER_ID` *and* the clinic row (`Clinic.waPhoneNumberId` — RUNBOOK §2 step 3) - **WhatsApp Business Account ID** → `WA_BUSINESS_ACCOUNT_ID` 4. Add the clinic's real number: *API Setup → Add phone number* → verify via SMS/voice. (The test number works for development; the real number is needed for the pilot.) 5. App Settings → Basic → **App Secret** → `WA_APP_SECRET`. ### 3.2 Permanent access token (system user — temporary tokens die in 24h) 1. business.facebook.com → Settings → **Users → System users** → Add. Name it `mawid-api`, role **Admin**. 2. Select it → **Assign assets**: your app (Full control / Manage app) *and* your WhatsApp account (Manage WhatsApp Business accounts). 3. **Generate token**: select the app, token expiration **Never**, permissions `whatsapp_business_messaging` + `whatsapp_business_management`. 4. Copy once → `WA_ACCESS_TOKEN`. Rotation procedure: RUNBOOK §3. ### 3.3 Webhook 1. Choose a verify token: `openssl rand -hex 16` → `WA_WEBHOOK_VERIFY_TOKEN`, redeploy so the API knows it. 2. App dashboard → WhatsApp → **Configuration → Webhook**: - Callback URL: `https://api.yourdomain.com/webhooks/whatsapp` - Verify token: the value above → **Verify and save** (the API answers the GET handshake; a 403 here means token mismatch). 3. **Webhook fields**: subscribe to `messages` (that one field carries inbound messages, button replies, and delivery statuses). 4. Test: text the number, confirm the message appears in the dashboard Conversations page and `docker compose logs api` shows the inbound. ### 3.4 Message templates (required — reminders/waitlist offers are sent outside the 24h customer-service window) WhatsApp Manager (business.facebook.com/wa/manage) → Account tools → **Message templates** → Create. Category **Utility** for both. Create each in **Arabic, Turkish, and English** (same template name, three languages). **Template 1 — name `appointment_reminder`** (must match exactly) - Body (en): `Appointment reminder: {{1}} on {{2}}. Can you confirm you are coming?` - `{{1}}` = service name, `{{2}}` = local date/time (the code fills these) - tr: `Randevu hatırlatması: {{1}}, {{2}}. Katılımınızı onaylıyor musunuz?` - ar: `تذكير بموعدك: {{1}} يوم {{2}}. بتأكد الحضور؟` - Buttons: **Quick reply ×3**, in this order (payloads are set at send time by the API — the button *labels* here are what patients see): 1. `✅ Confirm` / `✅ Onayla` / `✅ تأكيد` 2. `❌ Cancel` / `❌ İptal` / `❌ إلغاء` 3. `🔁 Reschedule` / `🔁 Yeniden planla` / `🔁 تغيير الموعد` **Template 2 — name `waitlist_offer`** - Body (en): `A slot just opened for {{1}} on {{2}}! Would you like it? It is held for you for {{3}} minutes.` - tr: `{{1}} için {{2}} tarihinde bir yer boşaldı! İster misiniz? Bu teklif {{3}} dakika boyunca sizin için ayrıldı.` - ar: `فضي موعد لـ {{1}} يوم {{2}}! بدك ياه؟ العرض محجوز إلك لمدة {{3}} دقيقة.` - Buttons: **Quick reply ×2**: `Yes, take it ✅` / `No thanks ❌` (+ tr/ar). Submit and wait for approval (minutes to ~24h for Utility). Status must be **Approved** before reminders fire — until then sends outside the 24h window fail and the reminder job logs a failure. ### 3.5 Final wiring check `.env.production` now has all five `WA_*` values → `./deploy.sh .env.production` → run RUNBOOK §2 (onboard clinic, set `waPhoneNumberId`) → full E2E: book via WhatsApp chat, receive the (test-offset) reminder template, tap ✅ Confirm, see `confirmed` in the dashboard. --- ## Order of operations, condensed 1. VPS + DNS + first deploy (infra up, `/health` green over HTTPS) 2. OpenAI key in env → agent smoke test via the WhatsApp **test number** 3. Meta business verification (start early!) → app → permanent token → webhook → templates approved 4. Real clinic number added → clinic onboarded (RUNBOOK §2) → pilot E2E 5. Backup cron + Sentry DSN → done