POST /api/v1/admin/orgs/:slug/teams/:teamId/members
This page is auto-generated from
src/admin/routes.ts. Do not edit by hand — seedocs/dev/docs-auto-sync.md.
:warning: Heuristic extraction — not a contract. Response shapes below are mined from
c.json(...)literals in the handler via an AST walker. Computed fields, ternaries, and non-literal returns are shown as placeholders. Refer to the linked source file for the authoritative behaviour.
POST /api/v1/admin/orgs/:slug/teams/:teamId/members
Section titled “POST /api/v1/admin/orgs/:slug/teams/:teamId/members”Authentication
Section titled “Authentication”Requires the following capability(ies):
team_members:w
Middleware
Section titled “Middleware”Other middleware observed on this route (heuristic):
requireCapabilityadminBodyLimit
Description
Section titled “Description”--- Team members (#564 Phase 1) -------------------------------------------
CRUD over the team_members composite-PK table. Adds the IDENTITY-grouping layer on top of the existing team-as-policy-grouping surface — a team has members, each member has a per-team role.
All four endpoints are gated on team_members:w. The admin-token /
platform-admin caller carries the cap via role-defaults (owner + admin).
Org member/viewer principals require the team_members membership lookup
via canDoOnTeam — but the admin surface here is the platform-operator
view: today’s caller IS effectively org-admin scoped through the URL
:slug rebind, so requireCapability is the right gate. The dashboard-side
per-team route in Phase 2 is where canDoOnTeam earns its keep.
:teamId is validated to belong to this org BEFORE any mutation runs — matches the cross-tenant guard the existing PATCH …/projects/:id/team endpoint enforces. Cross-org membership writes (team in org A, member in org B) are rejected as 404 from both sides:
- team lookup → 404 if the team isn’t in this org.
- member lookup → 404 if the member isn’t in this tenant.
Idempotency: POST returns 409 on the composite-PK collision (member already on this team). PATCH/DELETE return 404 if the row doesn’t exist. POST /api/v1/admin/orgs/:slug/teams/:teamId/members Body: { “member_id”: “<tenant_members.id>”, “role”: “admin” | “member” | “viewer” } Adds a tenant-member to a team with a per-team role.
Responses
Section titled “Responses”HTTP 404
Section titled “HTTP 404”{ "error": "Org not found"}HTTP 404
Section titled “HTTP 404”{ "error": "Team not found for this org"}HTTP 400
Section titled “HTTP 400”{ "error": "Invalid JSON body"}HTTP 400
Section titled “HTTP 400”{ "error": "Body must be a JSON object"}HTTP 400
Section titled “HTTP 400”{ "error": "member_id is required"}HTTP 400
Section titled “HTTP 400”{ "error": "role must be 'admin', 'member', or 'viewer'"}HTTP 404
Section titled “HTTP 404”{ "error": "Member not found for this org"}HTTP 409
Section titled “HTTP 409”{ "error": "Member already on this team"}HTTP 201
Section titled “HTTP 201”{ "team_id": <member-expr>, "member_id": <member-expr>, "role": <member-expr>, "created_at": <call>}Source
Section titled “Source”- File:
src/admin/routes.ts - Line: 5136