# WhatsApp Service Gateway The main FastAPI application (port **7860**) is the **public API gateway**. It proxies every request under `/api/whatsapp/*` to the internal WhatsApp service (a Go/whatsmeow application), which is not exposed publicly. ```text Client | HTTP v Main FastAPI :7860 | /api/whatsapp/... v WhatsApp Service (internal, e.g. http://localhost:8231) | v Client ``` ## Route mapping The gateway forwards the **full path, query string, HTTP method and body** unchanged. All routes require the main application's API key (`Authorization: Bearer `). | Client request | WhatsApp service call | |---|---| | `GET /api/whatsapp/server/ok` | `GET /server/ok` (health) | | `POST /api/whatsapp/instance/create` | `POST /instance/create` (admin) | | `GET /api/whatsapp/instance/status` | `GET /instance/status` | | `POST /api/whatsapp/send/text` | `POST /send/text` | | `DELETE /api/whatsapp/instance/delete/:id` | `DELETE /instance/delete/:id` | No path transformation is applied: after the `/api/whatsapp` prefix is stripped, the rest of the path is appended to `WHATSAPP_SERVICE_URL`. ## Deployment model Single Docker container (Hugging Face Spaces style): the Go binary is compiled in a multi-stage `whatsapp-build` stage and embedded in the image at `/app/whatsapp-service/server`. `start.sh` launches it as a sibling process and `uvicorn` is the primary process. The gateway reaches it over loopback (`http://127.0.0.1:8231`). A crash of the WhatsApp service does **not** take the main app down: the gateway returns sanitized `502`/`503` responses until it recovers (restart loop every 5s, degraded mode). ## Gateway behaviour - **Auth:** the gateway requires the main app's Bearer API key. The WhatsApp service's own `apikey` header is forwarded as-is when provided by the client (per-instance token); otherwise the configured `WHATSAPP_SERVICE_GLOBAL_API_KEY` is injected (admin routes). - **Headers:** hop-by-hop headers and the gateway's `Authorization`/`Cookie` are never forwarded. Response headers are whitelisted — internal `Server`/`content-encoding`/hop-by-hop headers never reach the client. - **Errors:** connection failures → `502`; timeouts → `504`; disabled service → `503`. Upstream 4xx/5xx responses are passed through unchanged. Error bodies never contain internal hostnames, URLs, exceptions or stack traces. - **Payments/streaming:** responses are streamed back to the client (media/QR downloads are not buffered). ## Configuration | Variable | Default | Description | |---|---|---| | `WHATSAPP_SERVICE_ENABLED` | `false` | Enable the gateway routes. `start.sh` auto-enables it when the binary exists. | | `WHATSAPP_SERVICE_URL` | `http://localhost:8231` | Base URL of the internal WhatsApp service. | | `WHATSAPP_SERVICE_TIMEOUT` | `30` | Read/write/pool timeout for upstream calls (seconds). | | `WHATSAPP_SERVICE_CONNECT_TIMEOUT` | `5` | Connect timeout for upstream calls (seconds). | | `WHATSAPP_SERVICE_GLOBAL_API_KEY` | _(empty)_ | The WhatsApp service `GLOBAL_API_KEY`, injected as the default `apikey` header. | | `WHATSAPP_SERVICE_PORT` | `8231` | Port `start.sh` uses to launch the embedded binary (`SERVER_PORT`). | | `WHATSAPP_SERVICE_BINARY` | `/app/whatsapp-service/server` | Path to the embedded binary. | ### WhatsApp service env vars (centralized in the main app) There is exactly **one** configuration source: the main application's `.env` (or the deployment platform's environment). The Go service **never reads a `.env` of its own** — `start.sh` / the Docker image inject its variables as process environment, and `--dev` only optionally loads a local `.env` if one exists. Full reference: the WhatsApp section of `.env.example`. Values shared with the main app use the same names (`SUPABASE_URL`, `REDIS_URL`, ...). A few names are mapped automatically by `start.sh` so you only configure one value: | Centralized setting | Injected into Go service as | |---|---| | `SUPABASE_SERVICE_ROLE_KEY` | `SUPABASE_SERVICE_KEY` | | `WHATSAPP_SERVICE_GLOBAL_API_KEY` | `GLOBAL_API_KEY` | | `WHATSAPP_SERVICE_PORT` | `SERVER_PORT` | Other Go-service settings configured centrally: `SUPABASE_DB_URL`, `DATABASE_SAVE_MESSAGES`, `CLIENT_NAME`, `CONNECT_ON_STARTUP`, `DEBUG_ENABLED`, `LOG_TYPE`, `WEBHOOK_FILES`, `OS_NAME`, `WHATSAPP_VERSION_*`, `MINIO_*`, `AMQP_*`, `NATS_*`, `WEBHOOK_URL`, `PROXY_*`, `API_AUDIO_CONVERTER*`, `EVENT_IGNORE_*`, `QRCODE_MAX_COUNT`, `CHECK_USER_EXISTS`, `LOG_*`. > Configure `WHATSAPP_SERVICE_GLOBAL_API_KEY` (equivalently `GLOBAL_API_KEY`) > once — it feeds both the gateway's default `apikey` header and the Go > service's authentication. ## Health checks The main app's `GET /health` now includes a non-fatal `whatsapp_service` block: ```json {"configured": true, "reachable": true, "status": "ok"} ``` The WhatsApp service's own liveness endpoint is exposed through the gateway at `GET /api/whatsapp/server/ok`. ## Testing ```bash python -m pytest tests/test_whatsapp_gateway.py -v ``` Covers: gateway auth, forwarding (method/path/query/body/headers), apikey injection vs pass-through, upstream 4xx/5xx passthrough, 502/503/504 handling, response-header sanitization and the `/health` integration. A full stack test: ```bash docker build -t agentdeck-backend:test . # run with the WhatsApp env vars above + WHATSAPP_SERVICE_ENABLED=true ```