---
title: "Per-member profile tray (totals, activity calendar, top content, recent activity)."
method: GET
path: "/v1/orgs/{orgId}/analytics/people/{userId}"
tags: ["dashboards"]
---

# Per-member profile tray (totals, activity calendar, top content, recent activity).

`GET /v1/orgs/{orgId}/analytics/people/{userId}`

## Request

- Base URL: `http://localhost:8080`
- URL: `http://localhost:8080/v1/orgs/{orgId}/analytics/people/{userId}`
- Auth: one of:
  - API key in cookie `dvt_session`
  - HTTP bearer

The drill-down behind a `GET .../analytics/people` roster row (DVT-1498):
totals/previous, an 84-day activity calendar, an hour-of-day profile,
method mix, top read/edited dashboards, favorites, and recent activity
for a single member. Admin or owner only (audit:read). The `data`
object shape is frozen — field names are a client contract. Served
lazily per member (fetched on roster-row expansion) rather than
inlined onto the roster.

Views/edits/queries follow the same definitions as the sibling people/
content analytics endpoints: views = `dashboard_views`, edits =
`audit_logs` create/update, queries = `query_executions`, all keyed by
actor id and org-scoped.

`totals`/`previous`/`methodMix`/`topRead`/`topEdited`/`hourProfile`
are WINDOWED by `days` (current window vs. the immediately-prior
equal-length window for totals/previous). `weekCalendar` is a FIXED
trailing 84 calendar days, independent of `days`. `favorites` and
`recentActivity` are ALL-TIME, never windowed. `lastLoginAt` is
ALL-TIME, matching the roster's field on `OrgAnalyticsPeople`.

## Query parameters

- `days` integer

## Response `200`

Per-member profile tray.

- object
  - `data` PersonTray, required: Per-member profile tray (DVT-1498) — the drill-down behind a `GET .../analytics/people` roster row. All field names are a frozen client contract.
    - `userId` string, required: User UUID.
    - `name` string, required: display_name, else email, else 'unknown'.
    - `email` string, required
    - `role` string, required: Member's role name.
    - `joinedAt` string, date-time, required: organization_members.created_at.
    - `windowDays` integer, required: The requested window in calendar days (echo of the clamped `days` param), applied to totals/previous/methodMix/topRead/topEdited/ hourProfile only — weekCalendar/favorites/recentActivity are unaffected.
    - `generatedAt` string, date-time, required: RFC3339 UTC timestamp when this result was assembled.
    - `lastLoginAt` string, date-time, nullable: ALL-TIME most recent successful login_history row, or null if none on record.
    - `totals` PersonTotals, required: A views/edits/queries triple (DVT-1498).
      - `views` integer, required
      - `edits` integer, required
      - `queries` integer, required
    - `previous` PersonTotals, required: A views/edits/queries triple (DVT-1498).
      - `views` integer, required
      - `edits` integer, required
      - `queries` integer, required
    - `weekCalendar` object[], required: FIXED trailing 84-calendar-day daily activity series (independent of windowDays), oldest -> newest, every day present (even zero-event days) so a heatmap renders a continuous grid.
      - `date` string, required: "YYYY-MM-DD", UTC.
      - `events` integer, required: Combined views+edits+queries that day.
    - `hourProfile` integer[], required: 24 ints (hour 0-23, UTC), windowed combined views+edits+queries event counts.
    - `methodMix` MethodMix, required: An edit-method / actor-class breakdown of audit_logs create/update events (DVT-1498, collapsed to two classes DVT-1522): `human` = actor_type='user'; `agent` = any non-user actor_type. Replaces a prior three-way ui/mcp/api split keyed off audit metadata (oauth_client_id vs api_key_name), which systematically mislabeled the dvt MCP server's real traffic — it authenticates via a customer API key, stamping api_key_name — as "API" rather than "MCP", since oauth_client_id is only ever stamped by an unused code path.
      - `human` integer, required
      - `agent` integer, required
    - `topRead` object[], required: Windowed top 5 dashboards by views.
      - `dashboardId` string, required
      - `title` string, required: Dashboard title, or empty string when untitled.
      - `views` integer, required
    - `topEdited` object[], required: Windowed top 5 dashboards by edits. Sub-resource (page/element) edits attribute to their parent dashboard, same walk-up resolution as the org-wide recent-activity feed.
      - `dashboardId` string, required
      - `title` string, required: Dashboard title, or empty string when untitled.
      - `edits` integer, required
    - `favorites` object[], required: ALL-TIME favorited dashboards (user_favorites), most-recently-favorited first.
      - `dashboardId` string, required
      - `title` string, required: Dashboard title, or empty string when untitled.
    - `recentActivity` object[], required: ALL-TIME last 15 audit_logs events by this member, any action or resource type, most recent first.
      - `at` string, date-time, required
      - `action` string, required
      - `dashboardTitle` string, nullable: Best-effort resolved dashboard title via the same walk-up join as topEdited; absent/null for events with no resolvable dashboard (folder/org-level events, or a since-deleted dashboard).
  - `meta` Meta, required: Response metadata.
    - `requestId` string, required: Correlates with the X-Request-Id header.
    - `total` integer: Item count, present on collection responses.
    - `nextCursor` string, nullable: Opaque keyset-pagination cursor, present on paginated responses (e.g. GET /v1/dashboards) when more rows exist beyond this page. Pass it back as the `cursor` query param to fetch the next page; absent or null on the last page.
    - `roleResolveDegraded` boolean: Snowflake mode only (DVT-2260), on GET /v1/dashboards: present and true when the caller's live Snowflake role resolution failed or was unavailable — role-shared dashboards may be missing from this page. Omitted (not merely false) whenever resolution succeeded or the role-share arm does not apply (off snowflake mode, an org admin/owner, or off the ingress listener).

## Other responses

- `401`: Missing, invalid, or expired session.
- `403`: The caller's role does not permit this action.
- `404`: Resource does not exist.

---

[API](https://skmtc.dev/dvt/apis/dvt-rest-api.md) · [All operations](https://skmtc.dev/dvt/apis/dvt-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dvt/apis/dvt-rest-api/revisions/00016f504165?raw)
