# Umami v2 / v3 API capabilities — reference for flesh-out planning

**Verified against:** analytics.portnimara.com (Umami v3.1.0), 2026-05-19.
**Auth:** username/password → JWT via `POST /api/auth/login`, Bearer on every request, 1h TTL (we cache 55min).
**Companion code:** `src/lib/services/umami.service.ts` (currently wraps stats/pageviews/metrics/active).

Endpoints below are listed by topic area, with what we currently use, what's available but unused, and where it could plug into the CRM.

---

## 1. Stats & traffic snapshots — `/api/websites/:id/stats`

**Currently used.** Returns the flat aggregate over the requested window plus a `comparison` block for the prior window of equal length.

```json
{
  "pageviews": 2081, "visitors": 726, "visits": 872,
  "bounces": 457, "totaltime": 109519,
  "comparison": { "pageviews": 1935, "visitors": 642, ... }
}
```

**Unused fields we could surface:**

- `totaltime` — total seconds on site → derive avg session time (`totaltime / visits`).
- `bounces / visits` → bounce-rate KPI.
- Period-over-period deltas (already wired for trend arrows, but the _full_ comparison object has more we could use for a "what changed since last period" panel).

**Filters supported** (per Umami docs, mostly untested by us): `url`, `referrer`, `title`, `query`, `event`, `host`, `os`, `browser`, `device`, `country`, `region`, `city` — meaning every stats call can be sliced. **Big unlock:** show stats for a specific landing-page URL on the berth detail (e.g. `/berths/A12` stats), or filter by referrer to see which channels drove signed EOIs.

---

## 2. Time-series — `/api/websites/:id/pageviews`

**Currently used** for the trend chart. Returns `{pageviews: [{x, y}], sessions?: [{x, y}]}` (sessions only when `compare` is requested).

**Parameters:** `startAt`, `endAt`, `unit` (`year|month|day|hour`), `timezone`, `compare` (untapped), `filters` (untapped).

**Unused:** `compare=prev` gives the same series for the previous period — could power a dual-line "vs last period" overlay on the chart.

---

## 3. Top-N metrics — `/api/websites/:id/metrics`

**Currently used** for Top Pages / Referrers / Countries (limit 10). Returns `[{x, y}]`.

**Available `type` values** (we surface 4, Umami offers 17):

| Type                        | What it returns            | CRM use case                                              |
| --------------------------- | -------------------------- | --------------------------------------------------------- |
| `path`                      | Top URLs                   | ✅ Already shown (we mis-typed as `url`, now fixed)       |
| `referrer`                  | Top referring sites        | ✅ Already shown                                          |
| `country`                   | Visitors by country        | ✅ Already shown                                          |
| `browser` / `os` / `device` | Tech breakdown             | Not surfaced — useful for "is mobile traffic converting?" |
| `region` / `city`           | Geographic drill-down      | Strong fit for marina marketing                           |
| `language`                  | Visitor browser language   | Could feed i18n decisions                                 |
| `screen`                    | Resolution                 | Low value                                                 |
| `event`                     | Top custom events          | Big unlock — see §6 below                                 |
| `tag`                       | Event tags                 | Same                                                      |
| `query`                     | Top URL query strings      | UTM-debug surface                                         |
| `entry` / `exit`            | First/last page in session | Funnel analysis                                           |
| `title`                     | Top page titles (vs paths) | Better labels for non-slug URLs                           |
| `hostname`                  | Multi-domain sites         | Probably N/A                                              |
| `distinctId`                | Custom user identifiers    | If we ever pipe CRM user IDs into Umami                   |

---

## 4. Live visitors — `/api/websites/:id/active`

**Currently used** for the green-dot "N active right now" indicator. Returns `{visitors: number}` (last-5-min count).

**Alternative for richer realtime:** `/api/realtime/:websiteId` (live realtime feed) returns far more — current top URLs being viewed, current top countries, recent event stream, a 30-minute time-series, totals, plus a `timestamp` you can poll against. We could surface a "live" panel on the dashboard showing the most-viewed pages right now.

---

## 5. Sessions API — `/api/websites/:id/sessions/*`

**Not currently used.** Multiple endpoints worth integrating:

- `GET /sessions` — list every session in a range with full device/geo/visits/views columns. Pageable. Could power a "recent visitors" surface — see who's browsing the berth detail pages right now.
- `GET /sessions/stats` — summary aggregate (pageviews, visitors, visits, countries, events) keyed by session.
- `GET /sessions/:sessionId` — drill into a single session: device, OS, browser, country, subdivision, city, screen, language, firstAt, lastAt, visits, views, events, totaltime.
- `GET /sessions/:sessionId/activity` — full event timeline for one session (urlPath, eventName, referrerDomain, timestamps).
- `GET /sessions/:sessionId/properties` — custom session properties (email, name, etc. — if Umami's `identify()` is called from the marketing site).
- `GET /session-data/properties` + `/session-data/values` — aggregate custom session properties.
- `GET /sessions/weekly` — heatmap of session count by hour-of-week. Direct fit for an "engagement heatmap" widget.

**Big unlock:** if marketing site calls `umami.identify({email})` after EOI form submit, sessions can be linked back to a specific client. We could then show "this client's website journey" on their CRM detail page.

---

## 6. Events API — `/api/websites/:id/events/*`

**Not currently used.** Umami auto-tracks pageviews; custom events are fired explicitly (e.g. button clicks, form submits, video plays). Endpoints:

- `GET /events` — list custom events in a range.
- `GET /events/stats` — totals.
- `GET /events/series` — time-series per event.
- `GET /event-data/*` — aggregate over event payload properties.

**High-leverage CRM use cases:**

- Fire an event on the marketing site when someone clicks "Inquire about berth A12" → CRM Activity feed shows it in real-time on the inquiry record.
- Fire an event when someone downloads a brochure → see which brochures convert.
- Fire an event on EOI form-step completions → drop-off funnel analysis.

We'd need to add `umami.track('event-name', {payload})` calls on the marketing site (~1-2h work there) and a new admin surface to define/view these events.

---

## 7. Reports API — `/api/reports/*`

**Not currently used.** Umami's "saved reports" system. Endpoints:

- `GET /reports` + `GET /reports/:id` — list / retrieve saved reports.
- `POST /reports/insights` — slice-and-dice with arbitrary filters/dimensions.
- `POST /reports/funnel` — multi-step conversion analysis.
- `POST /reports/retention` — cohort retention over time.
- `POST /reports/utm` — UTM-tagged campaign performance.
- `POST /reports/journey` — most common navigation paths.
- `POST /reports/goals` — pageview/event-goal completion tracking.
- `POST /reports/revenue` — revenue attribution (if we fire `purchase` events with amount).
- `POST /reports/attribution` — first/last-click attribution modelling.

**Best fits for the CRM:**

- **Funnel report** for the EOI flow: `/berths → /berths/A12 → /inquire?berth=A12 → form submit → CRM EOI signed`. Surface drop-off percentages on the Pulse-style dashboard.
- **Journey report** to see "what paths do visitors take before signing an EOI?" — informs marketing-site IA.
- **UTM report** to plumb campaign attribution into the lead-source breakdown (currently CRM-side; could be cross-validated against marketing's UTM-tagged traffic).
- **Attribution report** to give Pipeline-by-Source a "first-click vs last-click" toggle.

---

## 8. Send events from CRM → Umami — `/api/send`

**Not currently used.** The collect endpoint accepts page hits + custom events from any client. CRM doesn't currently push events, but we could:

- Fire `umami.track('signed-eoi', {berth: 'A12', deal_value: 50000})` from the CRM after EOI completion — closes the loop between marketing-site funnel and CRM outcome.
- Fire `umami.track('contract-signed')`, `umami.track('deposit-received')` — full funnel visible in Umami without leaving it.

---

## 9. Multi-website + team admin — `/api/websites`, `/api/teams`, `/api/users`

**Not currently used.** We hard-code a single `umami_website_id` per port. Useful if a port runs multiple sites (e.g. main marina + residential subdomain): admin UI could list-and-pick from the configured Umami instance's websites instead of requiring manual ID copy-paste. Same for team membership.

---

## Prioritized opportunity list

Ranked by leverage-vs-effort, assuming the v3.1.0 fix in this commit is the baseline:

1. **Avg session time + bounce rate KPI tiles** (~20 min) — already in the `/stats` response, just need new tiles.
2. **`compare=prev` overlay on the pageviews trend chart** (~30 min) — dual-line "vs last period" surface.
3. **Country choropleth heatmap** (~4-6h) — already queued in Bucket 3 of the UAT findings doc as "World-map heatmap of Umami visitor origins."
4. **Surface top browsers / OS / devices** (~30 min) — additional `TopList` columns; pure UI work.
5. **Fire CRM-side events back into Umami** (~2-3h marketing-site + CRM hook) — closes the funnel between marketing and outcomes.
6. **EOI funnel via `/api/reports/funnel`** (~3-4h) — drop-off analysis from berth view → inquiry → signed EOI.
7. **Identify visitors → link sessions to clients** (~4-6h spread across marketing site + CRM detail surfaces) — biggest unlock but needs marketing-site changes.
8. **Sessions-list "recent visitors" panel** (~2-3h) — see who's browsing right now, drill into individual sessions.
9. **Saved-reports admin surface** (~6-10h) — let admins create + share Umami reports without leaving the CRM. Bigger product surface; defer until #1-#5 land.

---

## Service-layer additions needed to support the above

`src/lib/services/umami.service.ts` currently exports: `getStats`, `getPageviewsSeries`, `getMetric`, `getActiveVisitors`, `testConnection`. To unlock the opportunities above, add:

- `getSessions(portId, range, opts)` → `/sessions` (paged)
- `getSession(portId, sessionId)` → single-session drill-in
- `getSessionActivity(portId, sessionId, range)` → event timeline
- `getSessionsWeekly(portId, range)` → heatmap source
- `getEvents(portId, range)` + `getEventsStats(portId, range)` + `getEventsSeries(portId, range, eventName, unit)` → custom events
- `getRealtime(portId, range)` → `/api/realtime/:id` for the live panel
- `getReport(portId, reportType, body)` → POST wrappers for funnel/retention/journey/utm/goals/revenue/attribution
- `trackEvent(portId, name, payload)` → POST to `/api/send` for CRM → Umami event emission

Each is a thin wrapper around the existing `umamiFetch` (or a new `umamiPost` variant for the reports endpoints). The auth + JWT cache + retry logic already in place handles them all.

---

## Known gotchas (verified against v3.1.0)

- Metric `type=url` returns 400 — use `type=path` (handled in our code via back-compat alias).
- `/api/websites/:id/pageviews` returns `sessions` only when `compare` is in the query string — keep `.sessions` optional in TS types.
- Stats response is **flat** (`pageviews: number`), not nested (`pageviews: {value, prev}`). The v1 nested shape isn't in v2/v3.
- `/api/auth/login` returns a JWT with no `expires_in` field — we assume 1h and refresh proactively at 55min.
- Visiting `/api` in a browser returns nothing — base path has no GET handler. Use `/api/heartbeat` to check liveness.
- Filters are passed as query params (e.g. `&country=DE`), NOT as a JSON `filters` body, per actual API behaviour (docs occasionally show JSON which doesn't work for GET endpoints).
