# Architecture decisions

## 1. One control plane, two providers

A `channel` represents a WhatsApp sending identity. Its provider is either:

- `qr` - linked-device/Baileys
- `meta_cloud` - Official WhatsApp Business Platform Cloud API

The public message/campaign/automation layers target a `channel_id`, not a provider-specific endpoint. Provider-specific behavior lives behind `src/providers/`.

## 2. API first

The dashboard calls the same service layer concepts exposed by `/api/v1`. This prevents the UI from becoming the product boundary and keeps future API panels, mobile apps and WHMCS integrations straightforward.

## 3. MySQL is the source of truth

Messages are inserted before provider delivery. API callers receive a gateway message ID immediately and can query its lifecycle later. Retries therefore do not depend on an open HTTP request.

## 4. Idempotency belongs at message creation

`(account_id, idempotency_key)` is unique. A caller can safely retry a timed-out request without creating a second WhatsApp send.

Campaign recipient jobs also use deterministic idempotency keys.

## 5. Billing is enforced below the UI

Plan limits and subscription state are checked in services/workers, not only hidden buttons. Suspending an account therefore stops API sends, dashboard access and already-queued background sends.

## 6. Provider credentials stay server-side

- customer-facing responses never return `secret_config`
- Meta credentials are encrypted in MySQL
- QR credentials are isolated in a non-public session directory
- WHMCS uses a separate provisioning secret and never receives provider credentials

## 7. Official webhook endpoints are channel-specific

```text
/webhooks/meta/<channel-id>
```

This provides clean tenant routing. The configured Phone Number ID is also checked against webhook metadata when present.

## 8. Workload characteristics

The in-process worker supports controlled concurrency. Message claiming uses a MySQL transaction + locking read so multiple workers can safely claim different jobs.

Linked-device sends are serialized/paced per channel. Official API sends can use worker concurrency and are the intended high-volume path.

For much larger commercial volume, move workers into separately managed processes/containers and add provider-aware rate buckets; the database/API contract does not need to change.

## 9. Legacy reference intentionally not reproduced

The supplied project includes encrypted runtime PHP that is decrypted and evaluated with `eval()`. That makes auditing and commercial maintenance difficult. This implementation uses ordinary readable modules and does not decrypt, copy or reproduce that hidden code.
