# Africa's Talking WhatsApp Connector — Design

**Date:** 2026-09-29  
**Status:** Approved  
**Issue:** OVER-345  
**Goal:** Add Africa's Talking as a second WhatsApp provider without changing the existing Baileys/QR connector or shared ticket workflows.

## Decisions

| Topic | Choice |
|-------|--------|
| Coexistence | Baileys and Africa's Talking channels can run together |
| Channel model | Keep `kind = 'whatsapp'`; add a provider discriminator |
| Existing data | Backfill/default existing WhatsApp channels to `baileys` |
| Credentials | One deployment-level Africa's Talking account supplied through environment secrets |
| Provider limits | Africa's Talking supports direct conversations only; Baileys retains group and QR/session features |
| UI | Show provider and capability indicators; disable unsupported actions with an explanation |
| Shared behavior | Contacts, tickets, automation, media storage, realtime events, and audit behavior remain provider-neutral |

## Data model

Add a constrained provider field to `channels`:

- `provider = 'baileys' | 'africas_talking'` for WhatsApp channels.
- Existing WhatsApp rows default to `baileys`.
- Portal and email rows do not use WhatsApp provider behavior.
- Reuse `channels.phone_number` as the connected WhatsApp number.
- Reuse a unique inbound token for the Africa's Talking webhook URL; no API key is stored in the database.

Provider capabilities are computed by the API rather than persisted:

| Capability | Baileys | Africa's Talking |
|------------|---------|-------------------|
| Direct text | Yes | Yes |
| Supported media | Yes | Yes |
| Groups | Yes | No |
| QR pairing | Yes | No |
| Session reconnect | Yes | No |
| Templates | No | Yes |

The API returns the provider and capability map with channel responses so the frontend does not duplicate provider rules.

## Configuration and security

The API reads one account from deployment secrets:

- `AFRICASTALKING_USERNAME`
- `AFRICASTALKING_API_KEY`

Channel creation fails clearly when the selected provider is Africa's Talking and required secrets are unavailable. Secrets are never returned by the API, logged, stored in channel settings, or accepted from the browser.

The public webhook route uses a high-entropy per-channel token, rate limiting, request-size limits, schema validation, and provider authenticity checks where the documented webhook protocol supports them. Inbound media downloads use timeouts, size/type validation, and trusted provider locations before entering the existing storage pipeline.

## Provider boundary

Introduce a small WhatsApp provider interface behind `channelService`:

- Send direct text.
- Send supported media.
- Report static capabilities.
- Perform lifecycle operations supported by that provider.
- Perform group operations only when the capability is present.

The Baileys adapter wraps the existing worker calls. The Africa's Talking adapter calls the documented HTTP endpoints with the deployment credentials. Existing callers continue through `channelService`; they do not branch on provider.

Unsupported operations fail through a typed capability error. The API validates capabilities before invoking an adapter, and the UI disables the corresponding action.

## Inbound flow

1. Africa's Talking sends an event to the channel-specific webhook URL.
2. The route validates the token, payload, and any documented authenticity data.
3. A provider mapper converts the event to the existing normalized inbound message shape.
4. The shared ingestion service deduplicates on provider message ID.
5. Existing contact lookup, ticket creation/matching, media storage, talkback, alerts, and realtime events run unchanged.
6. Delivery/status events update existing message/channel state where the provider supplies a reliable identifier.

The existing Baileys `/internal/messages` route is changed only enough to call the same shared ingestion service. This avoids maintaining two ticket-creation pipelines.

## Outbound flow

1. Ticket routes and automation call the existing `channelService` send methods.
2. `channelService` resolves the channel's provider adapter.
3. The adapter maps direct text or media to the provider request.
4. The provider message ID is retained for deduplication and status correlation.
5. Success and failure continue through the existing message/realtime response model.

An Africa's Talking authentication or request failure must not be reported as a successful send. Group-ticket creation and group replies reject Africa's Talking channels before ticket or message side effects occur.

Template creation/administration is not part of Baileys parity and is outside this change. The capability indicator documents provider support; existing direct-message workflows use the message types allowed by the provider and return its actionable policy error when a template is required.

## Channel lifecycle and UI

“Add WhatsApp” first asks the user to select a provider:

- **Baileys:** preserve the current create, QR, reconnect, and restore-session flow.
- **Africa's Talking:** collect channel name and WhatsApp number, create the webhook token, and display the webhook URL to configure in Africa's Talking.

Channel list and detail views show the provider and capability badges. Group and QR/reconnect controls are unavailable for Africa's Talking with concise explanatory text. Connected/disconnected status remains visible for both providers; Africa's Talking authentication failures mark the channel disconnected and a later successful provider interaction restores connected status.

## Error handling

- Reject unknown providers and malformed provider payloads.
- Treat duplicate inbound events as successful no-ops.
- Return typed, user-readable errors for missing deployment configuration, authentication failure, unsupported capability, invalid recipient, unsupported media, and provider timeout.
- Use bounded request timeouts and do not include credentials or raw sensitive payloads in logs.
- Preserve enough provider response context for server-side diagnosis without exposing secrets.

## Testing

Automated coverage includes:

- Migration default/backfill and provider constraints.
- Capability maps and provider selection.
- Baileys adapter regression tests around existing worker calls.
- Africa's Talking request mapping for direct text and supported media.
- Webhook validation, normalization, token isolation, and deduplication.
- Shared inbound ticket behavior for both providers.
- Authentication, timeout, malformed event, duplicate event, and unsupported group cases.
- Channel create/list/detail API responses.
- Frontend provider selection, webhook display, badges, and disabled actions.

Verification includes the API/unit suites, frontend build/type-check, migration application, browser smoke tests for both setup flows, and the repository's required TestSprite flow when the app is reachable.

## Documentation and rollout

Update the environment example, Docker deployment configuration, README, changelog, knowledge base, and feature scope documentation. Deployment order:

1. Add Africa's Talking secrets to the deployment.
2. Apply the provider migration.
3. Deploy API/frontend changes while retaining the Baileys worker.
4. Create an Africa's Talking channel and configure its generated webhook URL.
5. Smoke-test inbound/outbound direct text and media.
6. Confirm existing Baileys QR, direct, and group flows still work.

## Non-goals

- Emulating group APIs that Africa's Talking does not expose.
- Replacing or removing Baileys.
- Storing per-channel Africa's Talking API keys.
- Building template creation/approval administration.
- A generic third-party connector plugin platform.
