Problem statement
The admin organization routes are less capable than the other admin collections, and the gap blocks work in the dashboard.
Every organization route currently exposed, from openapi.json at 4bbed05:
GET /admin/organizations params: []
POST /admin/organizations params: []
GET /admin/organizations/{organizationId} params: [organizationId]
PATCH /admin/organizations/{organizationId} params: [organizationId]
GET /admin/organizations/{organizationId}/members params: [organizationId]
POST /admin/organizations/{organizationId}/members params: [organizationId]
PATCH /admin/organizations/{organizationId}/members/{userId} params: [organizationId, userId]
DELETE /admin/organizations/{organizationId}/members/{userId} params: [organizationId, userId]
Two gaps:
1. An organization cannot be deleted. The only DELETE in the group removes a member. There is no DELETE /admin/organizations/{organizationId}, so an organization created by mistake, or belonging to an offboarded tenant, can only be edited, never removed.
2. GET /admin/organizations accepts no query parameters. It returns { organizations, total } with no limit, offset, or search, so a caller receives whatever the endpoint returns and cannot request a window or ask the server to filter.
This is inconsistent with the neighbouring admin collections, which is what makes it feel unintended rather than deliberate:
| Endpoint |
Pagination |
Filtering |
GET /admin/auth-events |
limit, offset |
userId, type, from, to |
GET /admin/sessions |
limit, offset |
none |
GET /admin/organizations |
none |
none |
Proposed solution
- Add
DELETE /admin/organizations/{organizationId}, with explicit semantics for existing memberships: either refuse while members remain (409) or cascade and document it. Whichever is chosen, it should be stated in the OpenAPI description, since the caller has to warn the operator accordingly.
- Add
limit and offset to GET /admin/organizations, matching the shape /admin/sessions already uses (limit with a sane default and maximum, offset defaulting to 0). total is already returned, so the response shape does not need to change.
- Optionally add a
search parameter over name and slug.
Alternatives considered
The dashboard currently filters the returned array in memory. That is fine for a small deployment and is commented as such, but it cannot reach an organization the endpoint did not return, so it is a stopgap rather than a solution.
Leaving deletion out entirely was also considered. It is defensible if organizations are meant to be permanent, but nothing in the API or docs says so, and the member routes do support removal, so the current state reads as an omission rather than a policy.
Impact area
Developer experience
Additional context
Blocks fells-code/seamless-auth-admin-dashboard#219, which has the dashboard side of this: a delete action gated behind confirmation and step-up, and server-driven pagination through the existing table component. Nothing there can ship until these routes exist, since a Remove button that 404s or a pager that cannot fetch page two is worse than the current honest limitation.
Related: fells-code/seamless-auth-admin-dashboard#220 raises the same search question for /admin/sessions, which has limit/offset but no search either. Worth deciding once for both collections rather than answering it differently per endpoint.
Problem statement
The admin organization routes are less capable than the other admin collections, and the gap blocks work in the dashboard.
Every organization route currently exposed, from
openapi.jsonat4bbed05:Two gaps:
1. An organization cannot be deleted. The only
DELETEin the group removes a member. There is noDELETE /admin/organizations/{organizationId}, so an organization created by mistake, or belonging to an offboarded tenant, can only be edited, never removed.2.
GET /admin/organizationsaccepts no query parameters. It returns{ organizations, total }with nolimit,offset, or search, so a caller receives whatever the endpoint returns and cannot request a window or ask the server to filter.This is inconsistent with the neighbouring admin collections, which is what makes it feel unintended rather than deliberate:
GET /admin/auth-eventslimit,offsetuserId,type,from,toGET /admin/sessionslimit,offsetGET /admin/organizationsProposed solution
DELETE /admin/organizations/{organizationId}, with explicit semantics for existing memberships: either refuse while members remain (409) or cascade and document it. Whichever is chosen, it should be stated in the OpenAPI description, since the caller has to warn the operator accordingly.limitandoffsettoGET /admin/organizations, matching the shape/admin/sessionsalready uses (limitwith a sane default and maximum,offsetdefaulting to 0).totalis already returned, so the response shape does not need to change.searchparameter over name and slug.Alternatives considered
The dashboard currently filters the returned array in memory. That is fine for a small deployment and is commented as such, but it cannot reach an organization the endpoint did not return, so it is a stopgap rather than a solution.
Leaving deletion out entirely was also considered. It is defensible if organizations are meant to be permanent, but nothing in the API or docs says so, and the member routes do support removal, so the current state reads as an omission rather than a policy.
Impact area
Developer experience
Additional context
Blocks fells-code/seamless-auth-admin-dashboard#219, which has the dashboard side of this: a delete action gated behind confirmation and step-up, and server-driven pagination through the existing table component. Nothing there can ship until these routes exist, since a Remove button that 404s or a pager that cannot fetch page two is worse than the current honest limitation.
Related: fells-code/seamless-auth-admin-dashboard#220 raises the same search question for
/admin/sessions, which haslimit/offsetbut no search either. Worth deciding once for both collections rather than answering it differently per endpoint.