feat: add paginated API endpoint for groups (#27603)

backend-only changes from #27271; see that PR for summary of changes +
implementation details
This commit is contained in:
Andrew Aquino
2026-08-10 13:23:14 -07:00
committed by GitHub
parent ddb2799009
commit 6e07e2610f
19 changed files with 1189 additions and 0 deletions
+71
View File
@@ -2317,6 +2317,77 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members
To perform this operation, you must be authenticated. [Learn more](authentication.md).
## Get groups by organization (paginated)
### Code samples
```sh
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/paginated-groups \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /api/v2/organizations/{organization}/paginated-groups`
Unlike "Get groups by organization" (GET /organizations/{organization}/groups),
which authorizes each group individually via its ACL, this endpoint requires
organization-wide group read permission and does no per-group filtering. It is
therefore not a drop-in replacement: callers without org-wide group read receive
an error rather than a filtered subset.
The `q` parameter uses the shared filter syntax. Bare terms (including multi-word)
perform a free-text search over group name and display name. `search:` is the only
accepted key and unknown keys return 400. Because group display names may contain
colons, a value with a colon must be quoted, e.g. `search:"team: frontend"`; an
unquoted colon fails with `Query element "team:" cannot start or end with ':'`.
This endpoint returns group summaries without the member roster: each group
carries only `total_member_count` and no `members` field. Callers that need the
roster use the group members endpoint (GET /groups/{group}/members).
### Parameters
| Name | In | Type | Required | Description |
|----------------|-------|--------------|----------|-------------------------------------------------------------|
| `organization` | path | string | true | Organization ID or name |
| `q` | query | string | false | Search query (see description for syntax and colon-quoting) |
| `limit` | query | integer | false | Page limit |
| `offset` | query | integer | false | Page offset |
| `after_id` | query | string(uuid) | false | After ID |
### Example responses
> 200 Response
```json
{
"count": 0,
"groups": [
{
"avatar_url": "http://example.com",
"display_name": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization_display_name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"organization_name": "string",
"quota_allowance": 0,
"source": "user",
"total_member_count": 0
}
]
}
```
### Responses
| Status | Meaning | Description | Schema |
|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------|
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.PaginatedGroupsResponse](schemas.md#codersdkpaginatedgroupsresponse) |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
## Serve provisioner daemon
### Code samples
+61
View File
@@ -9778,6 +9778,67 @@ Only certain features set these fields: - FeatureManagedAgentLimit - FeatureAgen
| » `[any property]` | array of string | false | | |
| `organization_assign_default` | boolean | false | | Organization assign default will ensure the default org is always included for every user, regardless of their claims. This preserves legacy behavior. |
## codersdk.PaginatedGroup
```json
{
"avatar_url": "http://example.com",
"display_name": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization_display_name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"organization_name": "string",
"quota_allowance": 0,
"source": "user",
"total_member_count": 0
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|-----------------------------|----------------------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `avatar_url` | string | false | | |
| `display_name` | string | false | | |
| `id` | string | false | | |
| `name` | string | false | | |
| `organization_display_name` | string | false | | |
| `organization_id` | string | false | | |
| `organization_name` | string | false | | |
| `quota_allowance` | integer | false | | |
| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | |
| `total_member_count` | integer | false | | Total member count is the number of members in the group, shown even when the caller cannot read individual members. The roster itself is not returned by this endpoint. |
## codersdk.PaginatedGroupsResponse
```json
{
"count": 0,
"groups": [
{
"avatar_url": "http://example.com",
"display_name": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization_display_name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"organization_name": "string",
"quota_allowance": 0,
"source": "user",
"total_member_count": 0
}
]
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|----------|-------------------------------------------------------------|----------|--------------|-------------|
| `count` | integer | false | | |
| `groups` | array of [codersdk.PaginatedGroup](#codersdkpaginatedgroup) | false | | |
## codersdk.PaginatedMembersResponse
```json