## Summary

This document explains:

- What the issue was (why the UI showed `{"detail":"Not Found"}` / no data).
- What changes were made to fix it.
- Where the changes live in the codebase.
- How to validate the fix in production.

> Note: All secrets/tokens/passwords are intentionally **redacted** in this document.

---

## What the issue was (root cause)

### 1) Frontend called `/telephony/*` APIs that did not exist on the running backend

The UI pages:

- `dashboard-frontend/src/pages/settings/TelephonyIntegration.jsx`
- `dashboard-frontend/src/pages/settings/CallSync.jsx`

were calling endpoints like:

- `GET /api/telephony/{bid}/integrations`
- `POST /api/telephony/{bid}/integrations`
- `DELETE /api/telephony/{bid}/integrations/{provider}`
- `GET /api/telephony/{bid}/preview`
- `POST /api/telephony/{bid}/sync-to-db`

But the running backend behind `https://pca.syntheon.in/api/*` was:

- systemd: `mcube-ai-fastapi.service`
- gunicorn: `fastapi_app:app`
- code: `dashboard-backend/fastapi_app.py`

and it originally had **no `/telephony/*` routes**, so those requests returned:

```json
{"detail":"Not Found"}
```

### 2) Even after the new endpoints existed, preview returned `table: null` because the source DB was wrong

After adding `/telephony/*` APIs, the preview endpoint was returning:

```json
{"source":"archive","table":null,"count":0,"records":[]}
```

This happens when the source DB connection is pointing to a database that does **not** contain the expected tables:

- `{source_bid}_callhistory` or `{source_bid}_call_history` (history)
- `{source_bid}_callarchive` or `{source_bid}_call_archive` (archive)

In the environment, `SYNC_SOURCE_DB_*` was pointing to a DB that had **no `8329_*` tables**, so the preview logic could not resolve a table name.

---

## What was changed (fix)

### A) Implemented the missing `/telephony/*` endpoints in FastAPI

File changed:

- `dashboard-backend/fastapi_app.py`

Endpoints added:

- **Integrations**
  - `GET /telephony/{bid}/integrations`
  - `POST /telephony/{bid}/integrations`
  - `DELETE /telephony/{bid}/integrations/{provider}`
- **Preview**
  - `GET /telephony/{bid}/preview`
    - Supports: `limit`, `date_from`, `date_to`, `dialstatus`, `direction`
    - Uses `callarchive` when `date_from` + `date_to` present, else uses `callhistory`
- **Push to raw_calls**
  - `POST /telephony/{bid}/sync-to-db`
    - Accepts `{ records: [...] }`
    - Inserts/updates rows in `{bid}_raw_calls` using `ON DUPLICATE KEY UPDATE`

Auth behavior:

- Missing header -> `401 Missing authorization header`
- Invalid/expired token -> `401 Invalid or expired token`
- Non-admin user for write endpoints -> `403 Business admin access required`

### B) Added per-business telephony integration storage in MySQL (encrypted password)

File changed:

- `dashboard-backend/db_handler.py`

DB table added (created automatically on first use):

- `business_telephony_integrations`

Purpose:

- Store the active telephony provider per business.
- Store per-business source DB connection settings (host/port/user/password/db name).
- Encrypt source DB password at rest using the same Fernet scheme already used for CRM secrets.

Helper methods added:

- `ensure_telephony_integrations_table()`
- `list_telephony_integrations(bid)`
- `upsert_telephony_integration(bid, provider, source_bid, config, is_active)`
- `delete_telephony_integration(bid, provider)`
- `set_active_telephony_provider(bid, provider)` (ensures only one active provider per bid)

### C) Deployment/runtime issue fixed: port 8002 was held by old gunicorn processes

After restarting `mcube-ai-fastapi.service`, systemd logs showed:

- `Connection in use: ('0.0.0.0', 8002)`

Cause:

- Old gunicorn master/worker processes from an earlier run were still bound to port 8002.

Resolution:

- Stop systemd service
- Kill the old gunicorn processes
- Start systemd service again

---

## How to validate (smoke tests)

### 1) Confirm routes exist (no longer 404)

Without auth header:

- `GET https://pca.syntheon.in/api/telephony/8329/integrations`
  - Expected: `401 Missing authorization header`

With a valid token:

- `GET https://pca.syntheon.in/api/telephony/8329/integrations`
  - Expected: `200` and `{ "integrations": [...] }`

### 2) Connect telephony integration with per-business config

Call:

- `POST https://pca.syntheon.in/api/telephony/8329/integrations`

Body shape:

```json
{
  "provider": "mcube2",
  "source_bid": "8329",
  "config": {
    "host": "<SOURCE_DB_HOST>",
    "port": 3306,
    "user": "<SOURCE_DB_USER>",
    "password": "<SOURCE_DB_PASSWORD>",
    "database": "<SOURCE_DB_NAME>"
  }
}
```

Expected:

- `200` with success message

### 3) Preview calls (table must not be null)

Archive:

- `GET /api/telephony/8329/preview?limit=5&date_from=YYYY-MM-DD&date_to=YYYY-MM-DD&dialstatus=ANSWER`
  - Expected: `200` with `table: "8329_callarchive"` and non-empty `records` when data exists

History:

- `GET /api/telephony/8329/preview?limit=5&dialstatus=ANSWER`
  - Expected: `200` with `table: "8329_callhistory"` and non-empty `records` when data exists

If you still see `table: null`, it means the configured source DB does not have the `{source_bid}_callarchive`/`{source_bid}_callhistory` tables.

---

## Files changed

- `dashboard-backend/fastapi_app.py`
  - Added `/telephony/*` API routes (integrations, preview, sync-to-db)
  - Added request models for telephony endpoints
  - Added helpers to fetch from configured source DB

- `dashboard-backend/db_handler.py`
  - Added `business_telephony_integrations` table + CRUD helpers

---

## Previously observed symptoms (before fix)

- Browser console/network:
  - `POST /api/telephony/{bid}/integrations` -> `404 {"detail":"Not Found"}`
  - `GET /api/telephony/{bid}/integrations` -> `404 {"detail":"Not Found"}`

- Vite websocket errors in console (`[vite] failed to connect to websocket`) were separate and not the telephony API issue.

