From 8b6aa89a1aa457ac8707dddf75522bc32fd03a4f Mon Sep 17 00:00:00 2001 From: Junior Dev Agent <288084488+junior-ai-bot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 06:55:05 +0000 Subject: [PATCH 1/2] feat(API): document GET /projects/{project_id}/members list endpoint STRINGS-3471 The project-scoped member list endpoint existed in strings-app but was missing from the published spec, so it could only be discovered by guessing the path from the documented PATCH sibling. It requires only the `read` scope, unlike the account-level members endpoints which require `team.manage`, making it the fallback path for callers without account-admin permissions. Adds paths/members/index_project.yaml with the q, job_id, page and per_page query parameters plus the Link/Pagination headers, and a new project_member schema reflecting the actual response shape (includes position, no project_role field). Co-Authored-By: Claude Sonnet 5 --- doc/compiled.json | 207 +++++++++++++++++++++++++++++++ paths.yaml | 3 + paths/members/index_project.yaml | 67 ++++++++++ schemas/project_member.yaml | 76 ++++++++++++ 4 files changed, 353 insertions(+) create mode 100644 paths/members/index_project.yaml create mode 100644 schemas/project_member.yaml diff --git a/doc/compiled.json b/doc/compiled.json index f1d1d0f3d..b5ad130db 100644 --- a/doc/compiled.json +++ b/doc/compiled.json @@ -21948,6 +21948,213 @@ "x-cli-version": "2.5" } }, + "/projects/{project_id}/members": { + "get": { + "summary": "List project members", + "description": "Get all members active in the project. Access token scope must include `read`.", + "operationId": "members/by_project", + "tags": [ + "Members" + ], + "parameters": [ + { + "$ref": "#/components/parameters/X-PhraseApp-OTP" + }, + { + "$ref": "#/components/parameters/project_id" + }, + { + "description": "Specify a query to search for members by name or email (including wildcards).", + "example": "jane*", + "name": "q", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "description": "Filter members to those assigned to the job identified by this id.", + "example": "abcd1234cdef1234abcd1234cdef1234", + "name": "job_id", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/page" + }, + { + "$ref": "#/components/parameters/per_page" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "title": "project_member", + "properties": { + "id": { + "type": "string" + }, + "email": { + "type": "string" + }, + "username": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "string" + }, + "role": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "last_activity_at": { + "type": "string", + "format": "date-time" + }, + "permissions": { + "type": "object" + }, + "default_locale_codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "teams": { + "type": "array", + "items": { + "$ref": "#/components/schemas/team_short" + } + }, + "spaces": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "projects_count": { + "type": "integer" + } + } + } + } + }, + "example": { + "id": "acbdacbdacbdacbdacbdacbd", + "email": "foo@bar.com", + "username": "myname", + "name": "My Name", + "position": "Lead Developer", + "role": "Manager", + "created_at": "2020-12-07T12:56:21Z", + "last_activity_at": "2020-12-09T12:56:21Z", + "permissions": [ + { + "create_upload": true, + "review_translations": true + } + ], + "default_locale_codes": [ + "en", + "fi" + ], + "teams": [ + { + "id": "04d36d845576b9d494d05e0b70fe813c", + "name": "Team 1", + "created_at": "2020-12-07T12:56:21Z", + "updated_at": "2020-12-07T12:56:21Z" + } + ], + "spaces": [ + { + "id": "04d36d845576b9d494d05e0b70fe813b", + "name": "Space2", + "created_at": "2020-12-07T12:56:21Z", + "updated_at": "2020-12-07T12:56:21Z", + "projects_count": 1 + } + ] + } + } + } + } + }, + "headers": { + "X-Rate-Limit-Limit": { + "$ref": "#/components/headers/X-Rate-Limit-Limit" + }, + "X-Rate-Limit-Remaining": { + "$ref": "#/components/headers/X-Rate-Limit-Remaining" + }, + "X-Rate-Limit-Reset": { + "$ref": "#/components/headers/X-Rate-Limit-Reset" + }, + "Link": { + "$ref": "#/components/headers/Link" + }, + "Pagination": { + "$ref": "#/components/headers/Pagination" + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "401": { + "$ref": "#/components/responses/401" + }, + "403": { + "$ref": "#/components/responses/403", + "description": "Forbidden. Returned when the access token lacks the `read` scope or when the requesting user is not allowed to list members in this project." + }, + "404": { + "$ref": "#/components/responses/404" + }, + "429": { + "$ref": "#/components/responses/429" + } + }, + "x-code-samples": [ + { + "lang": "Curl", + "source": "curl \"https://api.phrase.com/v2/projects/:project_id/members\" \\\n -u USERNAME_OR_ACCESS_TOKEN" + }, + { + "lang": "CLI v2", + "source": "phrase members by_project \\\n--project_id \\\n--access_token " + } + ], + "x-cli-version": "2.5" + } + }, "/projects/{project_id}/members/{id}": { "patch": { "summary": "Update a member's project settings", diff --git a/paths.yaml b/paths.yaml index e480dfa3a..eabdf4889 100644 --- a/paths.yaml +++ b/paths.yaml @@ -431,6 +431,9 @@ "$ref": "./paths/members/update.yaml" delete: "$ref": "./paths/members/destroy.yaml" +"/projects/{project_id}/members": + get: + "$ref": "./paths/members/index_project.yaml" "/projects/{project_id}/members/{id}": patch: "$ref": "./paths/members/update_settings.yaml" diff --git a/paths/members/index_project.yaml b/paths/members/index_project.yaml new file mode 100644 index 000000000..197c039da --- /dev/null +++ b/paths/members/index_project.yaml @@ -0,0 +1,67 @@ +--- +summary: List project members +description: |- + Get all members active in the project. Access token scope must include `read`. +operationId: members/by_project +tags: +- Members +parameters: +- "$ref": "../../parameters.yaml#/X-PhraseApp-OTP" +- "$ref": "../../parameters.yaml#/project_id" +- description: |- + Specify a query to search for members by name or email (including wildcards). + example: jane* + name: q + in: query + schema: + type: string +- description: Filter members to those assigned to the job identified by this id. + example: abcd1234cdef1234abcd1234cdef1234 + name: job_id + in: query + schema: + type: string +- "$ref": "../../parameters.yaml#/page" +- "$ref": "../../parameters.yaml#/per_page" +responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + "$ref": "../../schemas/project_member.yaml#/project_member" + headers: + X-Rate-Limit-Limit: + "$ref": "../../headers.yaml#/X-Rate-Limit-Limit" + X-Rate-Limit-Remaining: + "$ref": "../../headers.yaml#/X-Rate-Limit-Remaining" + X-Rate-Limit-Reset: + "$ref": "../../headers.yaml#/X-Rate-Limit-Reset" + Link: + "$ref": "../../headers.yaml#/Link" + Pagination: + "$ref": "../../headers.yaml#/Pagination" + '400': + "$ref": "../../responses.yaml#/400" + '401': + "$ref": "../../responses.yaml#/401" + '403': + "$ref": "../../responses.yaml#/403" + description: Forbidden. Returned when the access token lacks the `read` scope or when the requesting user is not allowed to list members in this project. + '404': + "$ref": "../../responses.yaml#/404" + '429': + "$ref": "../../responses.yaml#/429" +x-code-samples: +- lang: Curl + source: |- + curl "https://api.phrase.com/v2/projects/:project_id/members" \ + -u USERNAME_OR_ACCESS_TOKEN +- lang: CLI v2 + source: |- + phrase members by_project \ + --project_id \ + --access_token +x-cli-version: '2.5' diff --git a/schemas/project_member.yaml b/schemas/project_member.yaml new file mode 100644 index 000000000..1aa08b8bf --- /dev/null +++ b/schemas/project_member.yaml @@ -0,0 +1,76 @@ +--- +project_member: + type: object + title: project_member + properties: + id: + type: string + email: + type: string + username: + type: string + name: + type: string + position: + type: string + role: + type: string + created_at: + type: string + format: date-time + last_activity_at: + type: string + format: date-time + permissions: + type: object + default_locale_codes: + type: array + items: + type: string + teams: + type: array + items: + "$ref": "./team_short.yaml#/team_short" + spaces: + type: array + items: + type: object + properties: + id: + type: string + name: + type: string + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + projects_count: + type: integer + example: + id: acbdacbdacbdacbdacbdacbd + email: foo@bar.com + username: myname + name: My Name + position: Lead Developer + role: Manager + created_at: "2020-12-07T12:56:21Z" + last_activity_at: "2020-12-09T12:56:21Z" + permissions: + - create_upload: true + review_translations: true + default_locale_codes: + - en + - fi + teams: + - id: "04d36d845576b9d494d05e0b70fe813c" + name: "Team 1" + created_at: "2020-12-07T12:56:21Z" + updated_at: "2020-12-07T12:56:21Z" + spaces: + - id: "04d36d845576b9d494d05e0b70fe813b" + name: "Space2" + created_at: "2020-12-07T12:56:21Z" + updated_at: "2020-12-07T12:56:21Z" + projects_count: 1 From 4f31a870c9710593e0336dacbeab802c97e253ec Mon Sep 17 00:00:00 2001 From: Junior Dev Agent <288084488+junior-ai-bot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 06:17:06 +0000 Subject: [PATCH 2/2] fix(API): align project_member schema with actual serializer attributes Per reviewer feedback, the /projects/{project_id}/members serializer only exposes id, username, name, position, created_at, and updated_at. Remove the previously guessed fields (email, role, last_activity_at, permissions, default_locale_codes, teams, spaces) that don't exist on this endpoint's response. Co-Authored-By: Junior (Claude Sonnet 5) --- doc/compiled.json | 79 +------------------------------------ schemas/project_member.yaml | 54 +------------------------ 2 files changed, 4 insertions(+), 129 deletions(-) diff --git a/doc/compiled.json b/doc/compiled.json index b5ad130db..f63b5f0de 100644 --- a/doc/compiled.json +++ b/doc/compiled.json @@ -22002,9 +22002,6 @@ "id": { "type": "string" }, - "email": { - "type": "string" - }, "username": { "type": "string" }, @@ -22014,94 +22011,22 @@ "position": { "type": "string" }, - "role": { - "type": "string" - }, "created_at": { "type": "string", "format": "date-time" }, - "last_activity_at": { + "updated_at": { "type": "string", "format": "date-time" - }, - "permissions": { - "type": "object" - }, - "default_locale_codes": { - "type": "array", - "items": { - "type": "string" - } - }, - "teams": { - "type": "array", - "items": { - "$ref": "#/components/schemas/team_short" - } - }, - "spaces": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - }, - "projects_count": { - "type": "integer" - } - } - } } }, "example": { "id": "acbdacbdacbdacbdacbdacbd", - "email": "foo@bar.com", "username": "myname", "name": "My Name", "position": "Lead Developer", - "role": "Manager", "created_at": "2020-12-07T12:56:21Z", - "last_activity_at": "2020-12-09T12:56:21Z", - "permissions": [ - { - "create_upload": true, - "review_translations": true - } - ], - "default_locale_codes": [ - "en", - "fi" - ], - "teams": [ - { - "id": "04d36d845576b9d494d05e0b70fe813c", - "name": "Team 1", - "created_at": "2020-12-07T12:56:21Z", - "updated_at": "2020-12-07T12:56:21Z" - } - ], - "spaces": [ - { - "id": "04d36d845576b9d494d05e0b70fe813b", - "name": "Space2", - "created_at": "2020-12-07T12:56:21Z", - "updated_at": "2020-12-07T12:56:21Z", - "projects_count": 1 - } - ] + "updated_at": "2020-12-09T12:56:21Z" } } } diff --git a/schemas/project_member.yaml b/schemas/project_member.yaml index 1aa08b8bf..966061566 100644 --- a/schemas/project_member.yaml +++ b/schemas/project_member.yaml @@ -5,72 +5,22 @@ project_member: properties: id: type: string - email: - type: string username: type: string name: type: string position: type: string - role: - type: string created_at: type: string format: date-time - last_activity_at: + updated_at: type: string format: date-time - permissions: - type: object - default_locale_codes: - type: array - items: - type: string - teams: - type: array - items: - "$ref": "./team_short.yaml#/team_short" - spaces: - type: array - items: - type: object - properties: - id: - type: string - name: - type: string - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - projects_count: - type: integer example: id: acbdacbdacbdacbdacbdacbd - email: foo@bar.com username: myname name: My Name position: Lead Developer - role: Manager created_at: "2020-12-07T12:56:21Z" - last_activity_at: "2020-12-09T12:56:21Z" - permissions: - - create_upload: true - review_translations: true - default_locale_codes: - - en - - fi - teams: - - id: "04d36d845576b9d494d05e0b70fe813c" - name: "Team 1" - created_at: "2020-12-07T12:56:21Z" - updated_at: "2020-12-07T12:56:21Z" - spaces: - - id: "04d36d845576b9d494d05e0b70fe813b" - name: "Space2" - created_at: "2020-12-07T12:56:21Z" - updated_at: "2020-12-07T12:56:21Z" - projects_count: 1 + updated_at: "2020-12-09T12:56:21Z"