# 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).