---
title: "Admin Export Xlsx"
method: POST
path: "/api/v1/quick-spaces/admin/export"
tags: ["quickSpaces", "quickSpaces"]
---

# Admin Export Xlsx

`POST /api/v1/quick-spaces/admin/export`

The report as a workbook.

No `scope` (the body the dashboard sends today) returns the whole report as the same five-sheet
workbook it always has, with `user_ids` null or empty meaning every user. A `scope` returns ONE
section — overview, people, rankings, projects, shares, activities, one user or one project —
under the filters that section declares, always opening with a Context sheet that names the
window, the scope and every filter, including any that were sent and not used.

"Export selected" on the activity feed sends `scope="activities"` with `activity_ids`: the row
ids to export, verbatim. It is a list, not a query — every other activities filter is ignored,
and an empty list exports nothing.

## Headers

- `X-Admin-Session` string, nullable

## Request body

- AdminExportRequestDTO — Which SECTION of the dashboard the XLSX covers, over which window, under which filters. `scope` names the section. It is OPTIONAL and its absence is load-bearing: a body with no `scope` returns exactly the five-sheet workbook this endpoint has always returned, built from `user_ids`/`start`/`end` alone — which is the only body the shipped frontend sends. Every field added below is inert on that path. Widening it later (letting `search` quietly narrow the legacy workbook, say) would change a file an operator already relies on without them asking for it. Scopes and the filters each one reads — a filter that a scope does not name is IGNORED by it and is reported as such on the workbook's Context sheet, so a file can never claim a narrower or wider selection than it actually has: all (default) user_ids the legacy five sheets overview user_ids people user_ids, search, sort, order rankings user_ids, metric projects project_ids, user_ids (as assignee), search, sort, order shares share_ids, user_ids (as creator), search, sort, order activities activity_ids OR (activity_kinds, activity_source, user_ids (as actor), project_ids, share_ids, search); sort, order either way user user_ids, sort, order project project_ids An EMPTY selection is a selection. `project_ids=[]` is "everything" (nothing was picked), but a list naming only ids that do not exist narrows to nothing and produces empty data sheets — it never falls back to the whole workspace. `activity_ids` is the one field where `[]` is NOT "nothing was picked" — see its own description. `activity_ids` is how a TICKED SELECTION on the activity feed is exported, and it is not a filter — it is the list itself. The rows on that feed come from six populations in five tables and carry disjoint context (a team upload has a project and no share link, a client view has a share link and usually no project), so a selection spanning both families cannot be described by intersecting the facets its rows happen to share: doing that ANDed a project set against a share set and matched nothing, and "Export selected" returned an empty workbook. Row ids are unique across every family, so they are the only honest expression of a mixed pick. When it is present every other activities filter is ignored, and the Context sheet says so. Its ABSENCE and its EMPTINESS differ, unlike the id lists above. `null` means no rows were individually picked, so the facet filters decide. `[]` means the operator picked nothing, and it exports nothing — a selection that quietly became "everything" is the one direction this must never fail in. `start`/`end` are inclusive YYYY-MM-DD calendar days in UTC; both absent means the last 30 days, the same default every GET on this lane uses.
  - `scope` string, nullable — all (default) | overview | people | rankings | projects | shares | activities | user | project
  - `user_ids` string[], nullable
  - `project_ids` string[], nullable
  - `share_ids` string[], nullable
  - `activity_kinds` string[], nullable
  - `activity_ids` string[], nullable — activities scope: export EXACTLY these feed rows, by their `id` from /admin/activities ("<family>:<row id>"). Overrides every other activities filter. [] exports no rows.
  - `activity_source` string, nullable — client | team
  - `search` string, nullable
  - `sort` string, nullable
  - `order` string, nullable — asc | desc (default desc)
  - `metric` string, nullable — rankings: one board key
  - `start` string, nullable — YYYY-MM-DD, inclusive, UTC
  - `end` string, nullable — YYYY-MM-DD, inclusive, UTC

## Response `200`

Successful Response

## 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/export/post.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)
