# ShiftOps — Google Maps Platform keys & monitoring

**Last updated:** 2026-04-10

This document is the **single operational reference** for ShiftOps browser Maps usage: which key applies, where it is configured, how to rotate it safely, and how to monitor usage. It does **not** replace product-wide or SEO service-account docs (`docs/seo-strategy-2026/guides/API_SETUP_GUIDE.md`).

## Key identity (GCP)


| Field               | Value                                                                                    |
| ------------------- | ---------------------------------------------------------------------------------------- |
| **Console name**    | `website-maps-shiftops` (or successor after rotation)                                    |
| **Typical project** | `ordio-256916` (verify in Google Cloud Console)                                          |
| **Purpose**         | ShiftOps UI only: Maps JavaScript + Places + Static Maps + Maps Embed + Place Photo URLs |


## What the app loads (code)

ShiftOps reads the key **only** via `ordio_get_shiftops_google_maps_api_key()` in `v2/config/google-maps.php`. That value is exposed to the browser as `window.GOOGLE_MAPS_API_KEY` on:

- `v2/pages/shiftops.php` — loading flow, Places, static map thumbnails, photo tiles  
- `v2/pages/shiftops-report.php` — report: Embed iframe, Static fallback, Place photos  
- `v2/js/shiftops-pdf-generator.js` — PDF preview static map + photo URLs (uses `window.GOOGLE_MAPS_API_KEY` from the page)

**Do not** hardcode `AIzaSy…` in PHP/JS for ShiftOps; rotate any legacy keys that still appear in git history.

### Priority order (server)

1. Environment variable `**GOOGLE_MAPS_SHIFTOPS_API_KEY`** (preferred in production)
2. `**v2/config/google-maps-shiftops-api-key.php**` (gitignored; copy from `google-maps-shiftops-api-key.php.example`)
3. Fallback: `ordio_get_google_maps_api_key()` (site-wide key — avoid relying on this for ShiftOps in production)

## GCP — APIs to enable (API restrictions on the key)

Enable only what ShiftOps needs (principle of least privilege):

- Maps JavaScript API  
- Places API  
- Places API (New)  
- Maps Static API  
- Maps Embed API  
- Geocoding API — optional; enable only if you use server/client geocode calls with this key

## GCP — Application restrictions

For a **browser** key, use **HTTP referrers (websites)** and list at least:

- `https://www.ordio.com/*`  
- `https://ordio.com/*`

Add staging/preview hosts if applicable. **Do not** leave “Websites” selected with an empty list and assume behaviour is obvious — set explicit patterns.

### Local development (Docker / localhost)

Browsers may use **`http://localhost:PORT`** or **`http://127.0.0.1:PORT`**. Add **both** to the key’s HTTP referrer list, e.g.:

- `http://localhost:8003/*`
- `http://127.0.0.1:8003/*`

(Adjust port to match `docker-compose.yml` / your URL bar.)

If the **map / Places script loads** but **Place Photo** `<img>` requests fail with **403**, referrer mismatch is the most common cause. Confirm in DevTools → **Network** → filter `place/photo` → response status and body.

**Billing:** Place Photos (legacy) usage is billed on typical Maps Platform terms. The GCP project must have **billing enabled**; otherwise photo requests can fail even when the JS API loads.

**Docker env:** After editing `.env`, run `docker compose up -d` (recreate). Verify the variable reaches PHP:

```bash
docker compose exec wordpress env | grep GOOGLE_MAPS_SHIFTOPS
```

If empty, PHP will fall back to `google-maps-shiftops-api-key.php` or the site-wide Maps key — use the gitignored file as a reliable local fallback.

**Expired references:** `photo_reference` values can **expire**. If photos break after a long session, reload place details / run a fresh analysis so references are renewed (see [Place Photos (Legacy)](https://developers.google.com/maps/documentation/places/web-service/legacy/photos)).

## Server-side Places (PHP)

`v2/api/shiftops.php` may call Places HTTP APIs with the **same** loader today. If those requests fail with referrer-only keys, use a separate **IP-restricted** key (`maps-prod-server` pattern) and a dedicated env var when the codebase supports `**GOOGLE_MAPS_SERVER_API_KEY`** (future). Until then, ensure server egress is allowed for the key type you use.

## Rotation checklist

1. In Google Cloud → **Credentials**, create a new key (or rotate existing) with the same API + referrer restrictions.
2. Update **production only**: `GOOGLE_MAPS_SHIFTOPS_API_KEY` **or** `google-maps-shiftops-api-key.php` on the app server (never commit secrets).
3. Reload PHP / container so `getenv()` / file reads refresh.
4. Smoke-test: `/shiftops` (search + thumbnails), `/shiftops-report` (Standort-Karte + gallery).
5. Disable or delete the **old** key in GCP after a safe window.
6. Update this doc’s **Console name** / date if the key name changes.

## Monitoring & cost controls

Google does not offer per-key “custom RPS” sliders. Use:


| Mechanism                                 | Action                                                           |
| ----------------------------------------- | ---------------------------------------------------------------- |
| **Referrer + API restrictions**           | Limits abuse if a key string leaks                               |
| **Billing → Budgets & alerts**            | e.g. alert at 50% / 90% of monthly cap                           |
| **APIs & Services → Dashboard / Metrics** | Watch Maps / Places SKUs after deploys                           |
| **Quotas**                                | Per-API project quotas; adjust only after observing real traffic |


Record **baseline usage** (e.g. first full week after a key change) in your ops notes or ticket so future limits are data-driven.

## Docker (local stack)

The repo `docker-compose.yml` passes `**GOOGLE_MAPS_SHIFTOPS_API_KEY`** into the `wordpress` service from your environment.

1. Copy `**.env.example**` to `**.env**` in the project root (same folder as `docker-compose.yml`).
2. Set `GOOGLE_MAPS_SHIFTOPS_API_KEY=your-dev-key` (no quotes unless needed). `**.env` is gitignored.**
3. Restart containers so PHP sees the variable:
  `docker compose down && docker compose up -d`
4. Open ShiftOps at your mapped URL (e.g. `http://localhost:8003/...` per your routing).
5. In GCP, allow that origin on the key (e.g. `http://localhost:8003/*`, `http://127.0.0.1:8003/*`).

**Alternative:** keep using `**v2/config/google-maps-shiftops-api-key.php`** (gitignored) inside the mounted volume — no Compose change needed; env takes precedence when both are set.

## Related files (no secrets committed)


| File                                                 | Role                                       |
| ---------------------------------------------------- | ------------------------------------------ |
| `v2/config/google-maps.php`                          | `ordio_get_shiftops_google_maps_api_key()` |
| `v2/config/google-maps-shiftops-api-key.php.example` | Template only                              |
| `.gitignore`                                         | `google-maps-shiftops-api-key.php`         |


## Changelog


| Date       | Change                                                                                                                                                                         |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 2026-04-10 | Initial doc: ShiftOps-only key path, monitoring, rotation; aligned `shiftops.php` / `shiftops-report.php` / PDF JS with `window.GOOGLE_MAPS_API_KEY` (no hardcoded fallbacks). |
| 2026-04-10 | Place Photo URLs: use `photo_reference=` query param per legacy docs; troubleshooting for localhost referrers + billing + Docker env. |
