# GBP API Integration — Calls, Data & Structure

This document maps **every data point** the GMB Dashboard needs from Google, the **exact Google API calls** to make, and how the data flows from Google → Backend → Frontend.

---

## 1. Data We Are Pulling from Google Business Profile

Our dashboard tracks **8 core KPIs** per location plus **6-month historical trend** data.

### Current Snapshot Metrics (per location)

| # | Metric Name         | What It Means                                          | Mock Range         |
|---|---------------------|--------------------------------------------------------|--------------------|
| 1 | `total_views`       | Total profile views (Search + Maps combined)           | 10,000 – 18,000    |
| 2 | `search_impressions`| Times the listing appeared in Google Search results    | 3,000 – 8,000      |
| 3 | `map_views`         | Times the listing appeared in Google Maps              | 4,000 – 9,000      |
| 4 | `website_clicks`    | Clicks on the website link from the GBP listing        | 200 – 600          |
| 5 | `calls_made`        | Clicks on the phone number (call button)               | 40 – 150           |
| 6 | `direction_clicks`  | Clicks on "Get Directions"                             | 80 – 350           |
| 7 | `average_rating`    | Star rating (1.0 – 5.0)                                | 4.3 – 4.9          |
| 8 | `total_reviews`     | Total number of reviews on the listing                 | 150 – 400          |

### Historical Trend Data (per location)

| Field         | Description                                      |
|---------------|--------------------------------------------------|
| `month`       | First-of-month date (e.g. `2026-01-01`)          |
| `total_views` | Sum of profile views for that month              |

We store **6 months** of rolling history per location in the `trend_cache` table.

---

## 2. Google APIs Required

Three separate Google APIs provide the data we need:

### API 1: Business Profile Performance API (Metrics)

> **Purpose:** Fetch impression, click, and interaction metrics.

**Endpoint:**
```
GET https://businessprofileperformance.googleapis.com/v1/locations/{location_id}:fetchMultiDailyMetricsTimeSeries
```

**Query Parameters:**

| Parameter              | Value / Example                                |
|------------------------|------------------------------------------------|
| `dailyMetrics`         | `BUSINESS_IMPRESSIONS_DESKTOP_SEARCH`          |
| `dailyMetrics`         | `BUSINESS_IMPRESSIONS_MOBILE_SEARCH`           |
| `dailyMetrics`         | `BUSINESS_IMPRESSIONS_DESKTOP_MAPS`            |
| `dailyMetrics`         | `BUSINESS_IMPRESSIONS_MOBILE_MAPS`             |
| `dailyMetrics`         | `WEBSITE_CLICKS`                               |
| `dailyMetrics`         | `CALL_CLICKS`                                  |
| `dailyMetrics`         | `BUSINESS_DIRECTION_REQUESTS`                  |
| `dailyRange.start_date`| `year=2025, month=12, day=1`                   |
| `dailyRange.end_date`  | `year=2026, month=5, day=7`                    |

**Example Request:**
```http
GET https://businessprofileperformance.googleapis.com/v1/locations/12345678:fetchMultiDailyMetricsTimeSeries
    ?dailyMetrics=WEBSITE_CLICKS
    &dailyMetrics=CALL_CLICKS
    &dailyMetrics=BUSINESS_DIRECTION_REQUESTS
    &dailyMetrics=BUSINESS_IMPRESSIONS_DESKTOP_SEARCH
    &dailyMetrics=BUSINESS_IMPRESSIONS_MOBILE_SEARCH
    &dailyMetrics=BUSINESS_IMPRESSIONS_DESKTOP_MAPS
    &dailyMetrics=BUSINESS_IMPRESSIONS_MOBILE_MAPS
    &dailyRange.startDate.year=2025
    &dailyRange.startDate.month=12
    &dailyRange.startDate.day=1
    &dailyRange.endDate.year=2026
    &dailyRange.endDate.month=5
    &dailyRange.endDate.day=7
```

**Response Shape (simplified):**
```json
{
  "multiDailyMetricTimeSeries": [
    {
      "dailyMetric": "WEBSITE_CLICKS",
      "timeSeries": {
        "datedValues": [
          { "date": { "year": 2026, "month": 5, "day": 1 }, "value": "42" },
          { "date": { "year": 2026, "month": 5, "day": 2 }, "value": "38" }
        ]
      }
    }
  ]
}
```

**How we map it to our metrics:**

| Google Metric                                                                    | Our `metric_name`      |
|----------------------------------------------------------------------------------|------------------------|
| `BUSINESS_IMPRESSIONS_DESKTOP_SEARCH` + `BUSINESS_IMPRESSIONS_MOBILE_SEARCH`     | `search_impressions`   |
| `BUSINESS_IMPRESSIONS_DESKTOP_MAPS` + `BUSINESS_IMPRESSIONS_MOBILE_MAPS`         | `map_views`            |
| `search_impressions` + `map_views`                                               | `total_views`          |
| `WEBSITE_CLICKS`                                                                 | `website_clicks`       |
| `CALL_CLICKS`                                                                    | `calls_made`           |
| `BUSINESS_DIRECTION_REQUESTS`                                                    | `direction_clicks`     |

---

### API 2: My Business API v4 (Reviews — Rating + Count)

> **Purpose:** Fetch the average rating and total review count.

**Endpoint:**
```
GET https://mybusiness.googleapis.com/v4/accounts/{account_id}/locations/{location_id}/reviews
```

**Query Parameters:**

| Parameter   | Value         |
|-------------|---------------|
| `pageSize`  | `1`           |

> We only need the summary data from the response (`averageRating`, `totalReviewCount`), not the individual reviews.

**Response Shape (simplified):**
```json
{
  "averageRating": 4.6,
  "totalReviewCount": 358,
  "reviews": [ ... ],
  "nextPageToken": "..."
}
```

**How we map it:**

| Google Field       | Our `metric_name`   |
|--------------------|---------------------|
| `averageRating`    | `average_rating`    |
| `totalReviewCount` | `total_reviews`     |

---

### API 3: My Business Account Management API (Location Listing)

> **Purpose:** List all locations under an account (for initial setup / discovery).

**Endpoint:**
```
GET https://mybusinessbusinessinformation.googleapis.com/v1/accounts/{account_id}/locations
```

**Response Shape (simplified):**
```json
{
  "locations": [
    {
      "name": "locations/12345678",
      "title": "Fenesta Dealer Gurugram",
      "storefrontAddress": {
        "locality": "Gurugram",
        "administrativeArea": "Haryana"
      }
    }
  ]
}
```

> This is used when a Client/Agency wants to discover what Location IDs are available under their GBP account.

---

## 3. Authentication (OAuth 2.0)

All Google APIs require OAuth 2.0 with the following scope:

```
https://www.googleapis.com/auth/business.manage
```

**Required setup in Google Cloud Console:**
1. Enable **Business Profile Performance API**
2. Enable **My Business API** (or successor)
3. Create OAuth 2.0 credentials
4. Set authorized redirect URI to: `https://gmb-insights.hashtechorange.com/api/auth/google/callback`

**Libraries already installed** (in `requirements.txt`):
- `google-api-python-client==2.118.0`
- `google-auth==2.28.0`
- `google-auth-oauthlib==1.2.0`

---

## 4. Data Flow: Google → Database → Frontend

```
┌─────────────────┐     ┌──────────────────────┐     ┌────────────────────┐
│  Google GBP API  │────▶│  sync.py             │────▶│  PostgreSQL        │
│  (Performance +  │     │  sync_location_      │     │                    │
│   Reviews)       │     │  metrics()           │     │  metrics_cache     │
└─────────────────┘     │                      │     │  trend_cache       │
                        └──────────────────────┘     └────────┬───────────┘
                                                              │
                        ┌──────────────────────┐              │
                        │  client/routes.py    │◀─────────────┘
                        │  dealer/routes.py    │
                        │  agency/routes.py    │
                        └──────────┬───────────┘
                                   │  JSON API
                        ┌──────────▼───────────┐
                        │  Frontend (app.js)   │
                        │  Charts + KPI Cards  │
                        └──────────────────────┘
```

### Sync Trigger Points

| Trigger                          | When                               | Function Called               |
|----------------------------------|-------------------------------------|-------------------------------|
| **New dealer added**             | Client clicks "Create Dealer"      | `sync_location_metrics()` (background task) |
| **New HQ location added**        | Client/Agency adds own location    | `sync_location_metrics()` (background task) |
| **Nightly cron** (APScheduler)   | Every day at 2:00 AM               | `sync_all_active_dealers()`   |

---

## 5. Database Tables Involved

### `metrics_cache` — Current KPI Snapshot
```
┌──────────────┬─────────────┬──────────────┬────────────────────┐
│ location_id  │ metric_name │ metric_value │ updated_at         │
├──────────────┼─────────────┼──────────────┼────────────────────┤
│ mock-loc-001 │ total_views │ 15230        │ 2026-05-07 02:00   │
│ mock-loc-001 │ calls_made  │ 134          │ 2026-05-07 02:00   │
└──────────────┴─────────────┴──────────────┴────────────────────┘
```
- **Key:** `UNIQUE(location_id, metric_name)`
- **Updated:** via `ON CONFLICT ... DO UPDATE` (upsert)

### `trend_cache` — Monthly Historical Views
```
┌──────────────┬────────────┬─────────────┐
│ location_id  │ month      │ total_views │
├──────────────┼────────────┼─────────────┤
│ mock-loc-001 │ 2026-05-01 │ 15230       │
│ mock-loc-001 │ 2026-04-01 │ 13707       │
│ mock-loc-001 │ 2026-03-01 │ 12184       │
└──────────────┴────────────┴─────────────┘
```
- **Key:** `UNIQUE(location_id, month)`
- **Month** is always 1st of the month for consistent `GROUP BY` aggregation

---

## 6. Internal API Endpoints (Our Backend → Frontend)

These are the dashboard endpoints that aggregate the cached data for the frontend:

| Method | Endpoint                                | Returns                                           |
|--------|-----------------------------------------|---------------------------------------------------|
| GET    | `/api/client/dashboard/global`          | Cumulative KPIs for all locations (dealers + HQ)   |
| GET    | `/api/client/dashboard/network`         | KPIs for sub-dealers only (excludes HQ)            |
| GET    | `/api/client/dashboard/hq`              | KPIs for the client's own HQ location              |
| GET    | `/api/client/dashboard/dealer/{id}`     | KPIs for a single dealer (drill-down view)         |
| GET    | `/api/dealer/dashboard`                 | Individual dealer's own location KPIs              |
| GET    | `/api/agency/dashboard/my-location`     | Agency's own internal location KPIs                |
| GET    | `/api/health`                           | Health check — `{"status": "ok"}`                  |

**Common response shape (all dashboard endpoints):**
```json
{
  "metrics": {
    "total_views": 55183,
    "search_impressions": 27361,
    "map_views": 30821,
    "website_clicks": 1330,
    "calls_made": 509,
    "direction_clicks": 1048,
    "weighted_avg_rating": 4.6,
    "total_reviews": 1220
  },
  "total_locations": 4,
  "trend_data": [
    { "month": "2025-12-01", "views": 36250 },
    { "month": "2026-01-01", "views": 43499 },
    { "month": "2026-03-01", "views": 58000 },
    { "month": "2026-04-01", "views": 65250 },
    { "month": "2026-05-01", "views": 72503 }
  ]
}
```

---

## 7. Current State: Mock vs Real

| Component              | Current State                                  | Production Replacement Needed           |
|------------------------|------------------------------------------------|-----------------------------------------|
| `mock_api.py`          | Returns random data                            | Replace with real Google API calls       |
| `gbp_api.py`           | **Empty file** — placeholder for real API code | Implement OAuth + Performance API calls  |
| `sync.py`              | Calls `mock_api.py`                            | Swap to call `gbp_api.py`               |
| `.env` → `USE_MOCK_DATA` | Set to `true`                                | Set to `false` for production            |

**To switch to real data:** Set `USE_MOCK_DATA=false` in `.env` and implement `gbp_api.py` with actual Google API calls using the endpoints documented above.
