# Incoming WhatsApp Business

Built locally for staff web, iOS and Android. The user chose an **existing WhatsApp Business number; Meta API setup is not ready**. The local preview remains disabled and uninitialized. No number has been registered, moved, disconnected or connected; no messages have been sent.

## Staff workflow

The Inbox includes an **Incoming WhatsApp** panel and a **Message channel** filter. Once activated, one configured business number receives text through Meta Cloud API webhooks. Each sender has a separate conversation with its own phone identity, assignment, company link, notes, immutable history and shared unsent reply draft. A reply reference never combines different senders. New received messages reopen closed conversations while preserving their draft and assignment. No profile name is treated as a verified company identity.

Select a received text message to review a supplier offer or customer wanted item. The existing manual review, optional requested AI suggestions and reviewed learning workflow are reused. Confirmation creates a draft, preserves the WhatsApp message ID and original text, and does not activate stock or send a response. Sender phone metadata is not added to the AI request; the message body itself may contain contact details. Staff still review all commercial values.

Media, documents, locations, interactive messages and unsupported text produce visible notices. They are never downloaded, transcribed or used as trading/AI evidence. Captions are not treated as complete source text. Staff cannot create a purported received WhatsApp message manually; use an internal note instead. Viewer accounts can inspect history but cannot change it, request extraction, create drafts or control the connection.

## Connection preparation for the existing number

1. Confirm ownership of the existing number, the Meta business assets and the person who administers them. Determine the supported Meta onboarding route for this specific existing Business app number, including whether the app/API coexistence route is available. Do not delete the app account, deregister the number or migrate it as part of this development setup. Number onboarding and preserving current access need a separate confirmed activation plan.
2. In the approved Meta setup, obtain the WhatsApp Business Account ID, Phone Number ID and app secret. Generate a separate long random webhook verify token. Store secrets on the API server, never in mobile/web public variables, source control or a shared conversation.
3. Deploy a properly secured public HTTPS API. The webhook path goes directly to FastAPI: `/webhooks/whatsapp`. The authenticated Next.js staff proxy deliberately does not expose or forward webhook routes. Configure the reverse proxy hostname/body/time limits and `PB_ALLOWED_HOSTS`; do not expose a local preview or use a temporary public tunnel for business traffic.
4. Back up the platform database and apply migration `f93ac16b20d8`. Configure these private server values; the numeric examples below are placeholders, not Phone Buzz identifiers:

```dotenv
PB_WHATSAPP_ENABLED=true
PB_WHATSAPP_BUSINESS_ID=123456789012345
PB_WHATSAPP_PHONE_ID=987654321098765
PB_WHATSAPP_APP_SECRET=REPLACE_WITH_META_APP_SECRET
PB_WHATSAPP_VERIFY_TOKEN=REPLACE_WITH_A_LONG_RANDOM_PRIVATE_VALUE
```

5. Initialize once from `platform/apps/api`, using an existing active staff administrator:

```sh
.venv/bin/python -m phonebuzz.whatsapp_setup --admin-email your-admin@example.com
```

This records the account/number identity and activation timestamp. It makes **no network call**, does not register the number with Meta and never resets an existing boundary. Only messages timestamped from that activation boundary onward are imported. Existing chat history is not backfilled. A changed configured ID or inactive/non-admin owner blocks receiving until reviewed by an operator.

6. Configure the HTTPS callback and matching verify token in Meta, and arrange the `messages` subscription for the correct WhatsApp Business Account using the supported Meta setup. Handshake verification alone does not prove subscription or receiving works. A separate controlled receiving test must verify signature, scope, first receipt, duplicate retry, visible source, company assignment and media notices before staff rely on this connector.

The callback implements Meta's GET challenge/verify-token exchange and POST HMAC-SHA256 signature check on the original bytes. The payload envelope and account/phone/message identifiers follow Meta's official references. See [Meta's webhook protocol reference](https://whatsapp.github.io/WhatsApp-Nodejs-SDK/api-reference/webhooks/start/) and [Meta's Cloud API examples](https://www.postman.com/meta/whatsapp-business-platform/documentation/wlk6lh4/whatsapp-cloud-api?entity=request-13382743-accf558f-2cde-4c15-8921-8fcb11b375ac). The archived SDK is not installed; the connector implements the protocol in Python.

No Graph API access token is needed by this receiving handler. Meta-side asset management/subscription may separately require appropriately scoped access; that token is not requested or stored here. Outbound sending and media retrieval need a later authenticated Graph API integration.

## Delivery guarantees and limits

- Signatures are checked before parsing or writing. Wrong signatures return 401; disabled/unconfigured ingress returns 404; oversized bodies return 413. Webhooks are capped at 1 MiB and 100 messages, with bounded envelopes and IDs. Plain text must be nonblank, valid and at most 16,000 characters; unsupported content is not truncated into trading evidence.
- Both business-account ID and phone-number ID must match configuration. Other scopes and status-only events create no messages. The connection identity is fixed at initialization.
- A connection row lock serializes one number's ingestion. Message ID uniqueness, source entry, thread mapping and audit commit atomically before success is acknowledged. Concurrent and repeated deliveries create one message; duplicate counts track additional deliveries. A different payload reusing an ID raises a visible conflict count and audit event while preserving the original. Staff must reconcile edits/conflicts using the original conversation.
- A callback batch is validated before any item is written. A malformed targeted message prevents partial acknowledgement. Valid unsupported message types are recorded as notices, not parse errors.
- The stored provider timestamp is distinct from receipt/history time; late deliveries appear in receipt order. Old pre-activation messages keep metadata/hash only and are excluded from conversations. Raw webhook bodies, contact profiles, media URLs and access secrets are not retained.
- Pausing returns 503 so the provider can retry; it does not acknowledge and discard incoming content. Meta retries are finite. Resume promptly and reconcile possible gaps after long pauses or outages. Disabling the connector or losing owner access also prevents successful receiving. There is no polling worker, automatic history replay, chat-history import or guarantee of recovery after provider retries expire.
- Handshake query strings are removed from the application request scope before response access logging. Configure the outer proxy/load balancer/APM to omit query strings and bodies on this route too; those systems see the request before the application. Prefer `uvicorn ... --no-access-log` and controlled structured operational logs in production. Never log verify tokens, signatures, app secrets or full webhook payloads.

Authenticated staff routes are `GET /v1/whatsapp`, `GET /v1/whatsapp/messages` (paginated) and administrator-only versioned `POST /v1/whatsapp/control` with pause/resume. No API returns secrets. The public callback authenticates via Meta's signature, not a staff session; the staff proxy still requires its session and same-origin mutations.

## Remaining activation and production gates

Meta setup for the existing number, HTTPS deployment, controlled live receiving, PostgreSQL contention/load checks, edge rate/body/time limits, monitoring and alerts for failed deliveries, owner lifecycle, backup restores, retention/export/deletion policy and physical iOS/Android behavior remain pending. All current tests use synthetic signed payloads and isolated data. Bundle exports are not signed distribution builds.

No outbound replies, campaigns, read receipts, templates, delivery tracking, contact opt-in automation, group intake, media download, OCR, audio transcription or historical chat migration are implemented. An official outbound milestone must add exact-recipient/content approvals, idempotent delivery handling, consent/suppression and applicable messaging rules before sending. Continue development with quotation preparation and approval workflows while Meta activation is pending.
