Projects live under teams, so GET /api/v1/teams/{team_id}/projects answers for
one team at a time. Anything that has to name a project anywhere in the
organization — an SCIM or SSO group mapping, a scope picker, a report that walks
the whole tenancy — would otherwise list the teams and then issue one request
per team. In an org with 40 teams that is 41 round trips to fill one list.
GET /api/v1/orgs/{org_id}/projects is that list in one request.
Request#
There are no query parameters. The organization id is the only input.
Response#
| Field | Type | Meaning |
|---|---|---|
id | uuid | the project |
team_id | uuid | the team that owns it |
team_name | string | that team’s name, carried on the row |
name | string | the project’s name, unique only within its team |
created_at | RFC 3339 | when the project was created |
Rows are ordered by team name and then project name, so the result arrives already grouped.
team_name is why this is one request and not two. A project name is
unique only inside its team, so two teams may each own a prod. A caller
rendering an org-wide list has to say which prod it means, and carrying the
team’s name on the row is what lets it group by team without fetching the team
list as well.
Authorization#
The project capability’s read action, checked at the organization scope:
a viewer membership on the org is enough, and a membership in one of its teams
is not. The answer spans every team, so the authority has to be the org’s.
This is the same capability GET /api/v1/teams/{team_id}/projects takes — that
route just checks it against the team you named. GET /api/v1/rbac/matrix
publishes the row, and GET /api/v1/rbac/effective?org_id=... reports whether
the calling account holds it here.
A caller without it is refused with 403, and an unauthenticated one with
401. Both bodies carry a message and nothing else, so tell them apart by the
status rather than by a field:
When to use the per-team route instead#
Use GET /api/v1/teams/{team_id}/projects when you already know the team — it
is narrower, and it is the route a team-scoped account can reach without any
org-level membership. Reach for the org-wide list only when the question really
is org-wide.