Admin
The admin domain is lk-wiz's platform-operator console: a single /admin
page for reviewing signups, managing accounts, and overseeing every
workspace on the platform — not just the ones the admin happens to belong
to.
This page describes product-facing behavior. For exact request/response shapes and error codes, see the Admin API reference. For AI-client access, see Admin MCP tools — a much smaller surface than the REST API. Backups and restore are a related but separate feature — see the Backups pages.
Every admin operation requires role: admin. There are no partial
admin permissions (e.g. "can view but not approve") — it's all-or-nothing,
checked fresh on every backend call.
Who can get in
role comes from the caller's JWT custom:role claim. Non-admins hitting
/admin see "Access denied. Admin role required." instead of the page;
non-admins calling any /api/admin/* route get an HTTP 403.
Role changes don't force a re-login. If an admin's role is downgraded to
member mid-session, their existing JWT still says admin until it
expires or refreshes — they can keep using admin endpoints for a while
after being demoted. There's also no backend guard stopping an admin from
demoting themselves — the "you can't remove your own admin role"
behavior described in the product stories isn't implemented as a backend
rule today.
Pending users
The Pending Users tab lists everyone with approved: false —
newly-registered accounts awaiting review.
- Approve — enables the user in Cognito, flips
custom:approvedtotrue, and DMs them on Slack ("Account Approved... You can now log in"). If any step (database write, Cognito call, or Slack DM) fails, the whole approve action fails — even though, in some failure modes, the database might already showapproved: true. - Reject — disables the user in Cognito and marks them
rejected. No notification is sent to the rejected user — there's currently no rejection email or Slack DM, despite that being a documented intent.
Both actions remove the user from the pending list immediately (the frontend just refetches both the pending and full user lists after either action succeeds).
All users
A table of every user (no search or filter control in the UI today — the frontend just renders the full unfiltered response) with Name, Email, Status, Role, and an enable/disable action button.
The enable/disable button is currently unreliable. The backend's computed
status field can only ever be pending, approved, or rejected — it
is never disabled, because enabling/disabling a user only touches
Cognito, not the DynamoDB record the list reads from. The UI button
checks "is this user's status disabled?" to decide its own label and
action — since that's always false, the button always shows Disable
and always sends enabled: false, regardless of the user's real Cognito
state. There is currently no way to see, from this table, whether a given
user is actually enabled or disabled in Cognito.
Changing a user's role (member ↔ admin) via the dropdown updates both
DynamoDB and the user's Cognito custom:role attribute immediately — it
takes effect for that user on their next token refresh, no forced logout.
Workspaces
The Workspaces tab lists every workspace on the platform (including soft-deleted ones), with owner name/email resolved server-side.
The list does not currently include a member count, even though the UI
renders "{count} member(s)" next to each workspace — the underlying
endpoint doesn't compute or return that number, so it shows up blank/
undefined in the current build. Member counts are computed elsewhere
(a single workspace's own settings page), just not surfaced here.
- Delete (with a confirm step) soft-deletes an active workspace — flips its status, does not touch members/posts/ideas.
- Recover brings a soft-deleted workspace back to active. Both actions are metadata-only; nothing about the workspace's content is ever removed by either.
Platform metrics
Four stat tiles at the top of the page: Total Users, Pending Approval, Workspaces, and Posts Published (30d) — all backed by live DynamoDB scans.
The underlying GET /api/admin/metrics endpoint also returns
total_posts, total_ideas, and active_users_last_7_days — but all
three are hardcoded to 0 today, not computed from real data. The
frontend doesn't currently render them (only the four tiles above), so
this gap isn't visible in the UI, but don't build new dashboards or
alerts assuming those three fields are real.
See also
- Admin API reference — full endpoint list, request/response shapes, and every documented gap/inconsistency called out above.
- Admin MCP tools —
admin_list_users/admin_approve_useronly; notably, approving a user through MCP does not call Cognito, so the user still can't actually log in afterward.