---
title: "Admin Activities"
method: GET
path: "/api/v1/quick-spaces/admin/activities"
tags: ["quickSpaces", "quickSpaces"]
---

# Admin Activities

`GET /api/v1/quick-spaces/admin/activities`

One chronological feed over both families of event — public share-link hits and the
workspace's own team activity — with the breakdowns counted over every matching row rather than
over the page.

`actor` on a row is null whenever the schema names no Quick Spaces person (every client row, an
unresolvable comment author, a project with more than one assignee); `actor_label` is always
populated and is the honest description. See admin_analytics' activity-feed section header.

`source` is decided PER ROW, not by the kind: a comment written through a public review page is
`client` even though it lives in the workspace's own comments table, resolved through the same
rule the review page uses to decide what a client may see.

`by_kind` and `top_actors` are the FILTER'S VOCABULARY, so each is counted over the rows
narrowed by every filter EXCEPT itself — otherwise picking one kind leaves one kind to pick from
— and `top_actors` names every person with activity rather than a top ten, ordered by count.

## Query parameters

- `start` string, nullable — YYYY-MM-DD, inclusive, UTC
- `end` string, nullable — YYYY-MM-DD, inclusive, UTC
- `kind` string, nullable — one kind, or a comma-separated list. client: view, download, markup, comment, status, invite. team: project_created, project_delivered, creative_uploaded, share_created, comment_written, signup. An unrecognised kind matches nothing rather than everything.
- `source` string, nullable — client | team (default: both)
- `actor` string, nullable — a Quick Spaces user id. Matches the RESOLVED actor only, so it excludes the client family — a public share link has no signed-in user to match against.
- `search` string, nullable — matches the event, actor, target, project, share or detail, and any id it names (row, target, project, share, asset). The row id is matched WITHOUT its family prefix, so `share` no longer returns every row whose id begins "share-activity:".
- `sort` string, nullable — at (default) | kind | source | actor | target | project
- `order` string, nullable — asc | desc (default desc)
- `page` integer
- `page_size` integer

## Headers

- `X-Admin-Session` string, nullable

## Response `200`

Successful Response

- AdminActivitiesResponseDTO — The feed, plus the three breakdowns computed over EVERY matching row rather than the page — a chart that changed when the operator turned the page would be a chart of the pagination. `truncated` says a source family hit its per-request ceiling (admin_analytics ACTIVITY_FAMILY_CEILING) and the OLDEST rows in the window were dropped. When it is true the counts below are counts of what was read, not of what exists, and the UI must say so. `by_kind` and `top_actors` are FACETS: each is counted over the rows filtered by everything EXCEPT itself, so the vocabulary a filter offers never shrinks to the choice already made. Both are the full vocabulary — `top_actors` names EVERY person with activity, not a top ten — because a name the list omits is a person the operator cannot select. It stays ordered by count, so a "busiest" display is its first N. `by_day` is not a facet and is a chart of the actual answer.
  - `range` AdminRangeDTO, required — The window every number on the response was computed over. Inclusive on both ends, UTC.
    - `start` string, date, required
    - `end` string, date, required
    - `days` integer
  - `rows` AdminActivityRowDTO[]
    - `id` string, required
    - `at` string, date-time, nullable
    - `source` string
    - `kind` string
    - `label` string
    - `actor` AdminUserRefDTO — A person named from another object — a project's assignee, a creative's uploader, a share link's owner. Identity only; every productivity number about them lives on their own row. Present as an object rather than a bare id because the two identity tables mean an id alone is not resolvable by the client: a monolith UUID and a Spaces 32-char hex look the same in JSON and only the server knows which directory to look in.
      - `user_id` string
      - `name` string
      - `email` string
      - `role` string
    - `actor_label` string
    - `target` AdminActivityTargetDTO — What the event happened TO. `type` is share | creative | project | comment | user.
      - `type` string
      - `id` string
      - `title` string
    - `project_id` string
    - `project_title` string
    - `share_id` string
    - `share_title` string
    - `asset_id` string
    - `detail` string
  - `total` integer
  - `page` integer
  - `page_size` integer
  - `by_kind` AdminActivityKindDTO[]
    - `kind` string, required
    - `label` string
    - `count` integer
  - `by_day` AdminActivityDayDTO[]
    - `date` string, date, required
    - `count` integer
  - `top_actors` AdminActivityActorDTO[]
    - `user_id` string
    - `name` string
    - `count` integer
  - `truncated` boolean

## Other responses

- `422` — Validation Error

## Changes

- **2026-09-02** `ff7f2b621b41` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/brandbooster/apis/fastapi/changes/api/v1/quick-spaces/admin/activities/get.md)

---

[API](https://skmtc.dev/brandbooster/apis/fastapi.md) · [All operations](https://skmtc.dev/brandbooster/apis/fastapi/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/brandbooster/fastapi/revisions/ff7f2b621b41/schema)
