From 1017bee1f8a064f4159a284d68dda80630b0305e Mon Sep 17 00:00:00 2001 From: Stephen Lumenta Date: Tue, 9 Jun 2026 19:36:18 +0200 Subject: [PATCH 1/3] feat(API): improve GET /projects/{project_id}/screenshots/{id} documentation Addresses rubric dimensions: conceptual_clarity, error_documentation, example_quality, frontmatter_metadata, reference_completeness, schema_completeness, self_contained_examples, stable_identifiers, vocabulary_consistency. Adds a structured description with usage guidance, id-field semantics, branch and access-control notes, and an error-code table. Replaces placeholder path examples with realistic ids and adds expected-response comments. Fixes operationId from screenshot/show to screenshots/show. Enriches error response descriptions with specific causes and remediations. Part of DEVEX-115. --- paths/screenshots/show.yaml | 100 ++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/paths/screenshots/show.yaml b/paths/screenshots/show.yaml index ad73d271..51909616 100644 --- a/paths/screenshots/show.yaml +++ b/paths/screenshots/show.yaml @@ -1,7 +1,47 @@ --- summary: Get a single screenshot -description: Get details on a single screenshot for a given project. -operationId: screenshot/show +description: | + Returns the full metadata for one screenshot within a project. + + Screenshots are image files uploaded to a project to provide translators with + visual context for the strings they are working on. Each screenshot can be + associated with one or more translation keys by attaching markers that point to + specific regions of the image. This endpoint retrieves a single screenshot + record by its unique string identifier, including the hosted image URL and the + count of translation-key markers currently attached to it. + + **When to use:** Use this endpoint to retrieve a screenshot's metadata, + verify its marker count, or resolve the hosted image URL before presenting it + in a localization workflow tool. + + **`id` field semantics:** The `id` in the response is the screenshot's string + code (for example, `d2e056aa9e70b01121f41693e344f5ee`), not a numeric primary + key. Use this value as the `id` path segment when calling other screenshot + endpoints (update, delete, marker operations). + + **Branches:** Pass the `branch` query parameter to scope the lookup to a + branch-isolated copy of the project. Omit it to read from the main (default) + branch. + + **Access control:** Requires the `read` OAuth scope. The authenticated user + must have at least read permission on the project. If the account's plan does + not include the Attachable Screenshots feature, the endpoint returns 403 for + all callers regardless of user permission level. + + **Error codes** + + The `message` field in error responses uses a stable string identifier in + UPPER_SNAKE_CASE format. Use the `message` value for programmatic error + handling; the human-readable text may change between releases. + + | HTTP status | Error code | Cause | Remediation | + |-------------|------------|-------|-------------| + | 400 | `bad_request` | Malformed request parameter or unparseable JSON body | Fix the request syntax and retry | + | 401 | `unauthorized` | Missing or invalid access token; 2FA code required but absent or wrong | Provide a valid access token; supply `X-PhraseApp-OTP` when 2FA is enabled | + | 403 | `forbidden` | Token lacks `read` scope; account unconfirmed; caller lacks read permission on the project; account plan does not include the Attachable Screenshots feature | Upgrade the OAuth token scope to `read`, confirm the account, or upgrade the account plan | + | 404 | `not_found` | Project or screenshot with the given `project_id` or `id` does not exist, or is not accessible to the caller | Verify both path parameters reference existing, accessible resources | + | 429 | `rate_limit_exceeded` | Request rate limit or concurrency limit exceeded | Reduce request rate; observe `X-Rate-Limit-Reset` before retrying | +operationId: screenshots/show tags: - Screenshots parameters: @@ -30,25 +70,65 @@ responses: "$ref": "../../headers.yaml#/X-Rate-Limit-Reset" '400': "$ref": "../../responses.yaml#/400" - '404': - "$ref": "../../responses.yaml#/404" + description: >- + Bad request. The `project_id`, `id`, or `branch` parameter is malformed, + or the request body cannot be parsed. Fix the request syntax and retry. '401': "$ref": "../../responses.yaml#/401" + description: >- + Unauthorized. The access token is missing or invalid. When the account has + two-factor authentication enabled, provide the current one-time password + (OTP) in the `X-PhraseApp-OTP` header. '403': "$ref": "../../responses.yaml#/403" - description: Forbidden. Returned when the access token lacks the `read` scope, when the requesting user is not allowed to read this screenshot, or when the account does not have the Attachable Screenshots feature. + description: >- + Forbidden. Returned in any of these situations: the access token does not + carry the `read` OAuth scope or the user account is unconfirmed; the + authenticated user does not have read permission on this project; or the + account's plan does not include the Attachable Screenshots feature. + '404': + "$ref": "../../responses.yaml#/404" + description: >- + Not found. No project matches `project_id`, or no screenshot matches `id` + within that project. Verify both path parameters reference existing, + accessible resources. '429': "$ref": "../../responses.yaml#/429" + description: >- + Too many requests. The per-user rate limit or concurrency limit has been + exceeded. Check the `X-Rate-Limit-Reset` response header for the Unix + timestamp at which the limit resets, then retry. x-code-samples: - lang: Curl source: |- - curl "https://api.phrase.com/v2/projects/:project_id/screenshots/:id?branch=my-feature-branch" \ + curl "https://api.phrase.com/v2/projects/3d7ce831a3e9e1b843dc16d36ee5b9f4/screenshots/d2e056aa9e70b01121f41693e344f5ee" \ -u USERNAME_OR_ACCESS_TOKEN + + # Expected response (HTTP 200): + # { + # "id": "d2e056aa9e70b01121f41693e344f5ee", + # "name": "Onboarding — step 1", + # "description": "First step of the user onboarding flow showing the project selection screen", + # "screenshot_url": "https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png", + # "created_at": "2024-03-15T10:22:31Z", + # "updated_at": "2024-03-18T14:05:12Z", + # "markers_count": 3 + # } - lang: CLI v2 source: |- phrase screenshots show \ - --project_id \ - --id \ - --branch my-feature-branch \ - --access_token + --project_id 3d7ce831a3e9e1b843dc16d36ee5b9f4 \ + --id d2e056aa9e70b01121f41693e344f5ee \ + --access_token 4a8e3f7c2b1d9e6a0c5f8b2d4e7a1c3f + + # Expected response (HTTP 200): + # { + # "id": "d2e056aa9e70b01121f41693e344f5ee", + # "name": "Onboarding — step 1", + # "description": "First step of the user onboarding flow showing the project selection screen", + # "screenshot_url": "https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png", + # "created_at": "2024-03-15T10:22:31Z", + # "updated_at": "2024-03-18T14:05:12Z", + # "markers_count": 3 + # } x-cli-version: '2.5' From aa2bf42d7ae11895ad09667a13c95d334b704444 Mon Sep 17 00:00:00 2001 From: Stephen Lumenta Date: Wed, 10 Jun 2026 13:33:34 +0200 Subject: [PATCH 2/3] feat(API): apply graded candidate fix for screenshots/show Adds inline 200 example, expands 403 error table with per-cause remediation, sharpens all error response descriptions, and updates conceptual description to reference feature requirement and branch behaviour explicitly. Dimensions addressed: conceptual_clarity, error_documentation, example_quality, reference_completeness, schema_completeness, self_contained_examples, stable_identifiers. Jira: DEVEX-115 --- paths/screenshots/show.yaml | 103 ++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/paths/screenshots/show.yaml b/paths/screenshots/show.yaml index 51909616..454b4dc0 100644 --- a/paths/screenshots/show.yaml +++ b/paths/screenshots/show.yaml @@ -1,46 +1,31 @@ --- summary: Get a single screenshot description: | - Returns the full metadata for one screenshot within a project. + Returns the full metadata for one screenshot in a project, identified by its string code. - Screenshots are image files uploaded to a project to provide translators with - visual context for the strings they are working on. Each screenshot can be - associated with one or more translation keys by attaching markers that point to - specific regions of the image. This endpoint retrieves a single screenshot - record by its unique string identifier, including the hosted image URL and the - count of translation-key markers currently attached to it. + A screenshot in Phrase is an image file uploaded to a project to give translators visual + context. Each screenshot can be linked to one or more translation keys via rectangular + marker regions. This endpoint retrieves the screenshot's name, description, hosted image + URL, and the count of markers currently attached to it. - **When to use:** Use this endpoint to retrieve a screenshot's metadata, - verify its marker count, or resolve the hosted image URL before presenting it - in a localization workflow tool. + Use this endpoint when you have a screenshot's `id` (its string code) and need its current + metadata or image URL. To discover screenshot codes, first call the list endpoint + (`screenshots/list`); to inspect the individual markers attached to a screenshot, call the + screenshot markers list endpoint. - **`id` field semantics:** The `id` in the response is the screenshot's string - code (for example, `d2e056aa9e70b01121f41693e344f5ee`), not a numeric primary - key. Use this value as the `id` path segment when calling other screenshot - endpoints (update, delete, marker operations). + **Feature requirement:** All screenshot endpoints require the Attachable Screenshots feature + to be enabled on the account. Requests made against an account without this feature return + `403` immediately, before any permission checks run. - **Branches:** Pass the `branch` query parameter to scope the lookup to a - branch-isolated copy of the project. Omit it to read from the main (default) - branch. + **Access control:** The access token must carry the `read` OAuth scope. The authenticated + user must have at least read permission on the project and on the screenshot resource. - **Access control:** Requires the `read` OAuth scope. The authenticated user - must have at least read permission on the project. If the account's plan does - not include the Attachable Screenshots feature, the endpoint returns 403 for - all callers regardless of user permission level. + **The `id` field is a string code, not a numeric primary key.** The value returned in the + response `id` field is the screenshot's code (e.g., `d2e056aa9e70b01121f41693e344f5ee`). + Pass this value to other screenshot endpoints such as update or delete. - **Error codes** - - The `message` field in error responses uses a stable string identifier in - UPPER_SNAKE_CASE format. Use the `message` value for programmatic error - handling; the human-readable text may change between releases. - - | HTTP status | Error code | Cause | Remediation | - |-------------|------------|-------|-------------| - | 400 | `bad_request` | Malformed request parameter or unparseable JSON body | Fix the request syntax and retry | - | 401 | `unauthorized` | Missing or invalid access token; 2FA code required but absent or wrong | Provide a valid access token; supply `X-PhraseApp-OTP` when 2FA is enabled | - | 403 | `forbidden` | Token lacks `read` scope; account unconfirmed; caller lacks read permission on the project; account plan does not include the Attachable Screenshots feature | Upgrade the OAuth token scope to `read`, confirm the account, or upgrade the account plan | - | 404 | `not_found` | Project or screenshot with the given `project_id` or `id` does not exist, or is not accessible to the caller | Verify both path parameters reference existing, accessible resources | - | 429 | `rate_limit_exceeded` | Request rate limit or concurrency limit exceeded | Reduce request rate; observe `X-Rate-Limit-Reset` before retrying | + **Branches:** Pass the `branch` query parameter to read from a branch project. Omit it to + read from the default branch. An invalid or inaccessible branch causes a `404` response. operationId: screenshots/show tags: - Screenshots @@ -61,6 +46,14 @@ responses: application/json: schema: "$ref": "../../schemas/screenshot.yaml#/screenshot" + example: + id: d2e056aa9e70b01121f41693e344f5ee + name: Onboarding step 1 + description: First step of the user onboarding flow showing the project selection screen + screenshot_url: https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png + created_at: '2024-03-15T10:22:31Z' + updated_at: '2024-03-18T14:05:12Z' + markers_count: 3 headers: X-Rate-Limit-Limit: "$ref": "../../headers.yaml#/X-Rate-Limit-Limit" @@ -71,33 +64,39 @@ responses: '400': "$ref": "../../responses.yaml#/400" description: >- - Bad request. The `project_id`, `id`, or `branch` parameter is malformed, - or the request body cannot be parsed. Fix the request syntax and retry. + Bad request. Returned when a query parameter is malformed or cannot be parsed. + Check the request URL and query parameter syntax; refer to the error `message` + field for the specific parsing problem. '401': "$ref": "../../responses.yaml#/401" description: >- - Unauthorized. The access token is missing or invalid. When the account has - two-factor authentication enabled, provide the current one-time password - (OTP) in the `X-PhraseApp-OTP` header. + Unauthorized. The request did not include a valid access token, or the token has + expired. Generate a new access token in Phrase account settings and pass it via + HTTP Basic auth or the `Authorization: Bearer` header. When 2FA is enabled, + supply the current OTP in `X-PhraseApp-OTP`. '403': "$ref": "../../responses.yaml#/403" - description: >- - Forbidden. Returned in any of these situations: the access token does not - carry the `read` OAuth scope or the user account is unconfirmed; the - authenticated user does not have read permission on this project; or the - account's plan does not include the Attachable Screenshots feature. + description: | + Forbidden. Returned in the following cases: + + | Cause | Remediation | + |-------|-------------| + | The Attachable Screenshots feature is not enabled on the account plan. | Upgrade the account plan or contact Phrase support to enable the Attachable Screenshots feature. | + | The access token lacks the `read` OAuth scope. | Re-authorize the OAuth application with the `read` scope. | + | The authenticated user does not have read permission on this project or screenshot. | Ask a project admin to grant at least Viewer access to this project. | '404': "$ref": "../../responses.yaml#/404" description: >- - Not found. No project matches `project_id`, or no screenshot matches `id` - within that project. Verify both path parameters reference existing, - accessible resources. + Not found. Returned when the project, screenshot, or the specified branch does not + exist or is not accessible to the requesting user. Verify the `project_id`, `id`, + and `branch` values. Use the screenshots list endpoint to retrieve valid screenshot + codes for the project. '429': "$ref": "../../responses.yaml#/429" description: >- - Too many requests. The per-user rate limit or concurrency limit has been - exceeded. Check the `X-Rate-Limit-Reset` response header for the Unix - timestamp at which the limit resets, then retry. + Rate limit exceeded. The account has exceeded its API request quota for the current + window. Reduce request frequency and implement exponential backoff. Check the + `X-Rate-Limit-Reset` response header for the Unix timestamp when the quota resets. x-code-samples: - lang: Curl source: |- @@ -107,7 +106,7 @@ x-code-samples: # Expected response (HTTP 200): # { # "id": "d2e056aa9e70b01121f41693e344f5ee", - # "name": "Onboarding — step 1", + # "name": "Onboarding step 1", # "description": "First step of the user onboarding flow showing the project selection screen", # "screenshot_url": "https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png", # "created_at": "2024-03-15T10:22:31Z", @@ -124,7 +123,7 @@ x-code-samples: # Expected response (HTTP 200): # { # "id": "d2e056aa9e70b01121f41693e344f5ee", - # "name": "Onboarding — step 1", + # "name": "Onboarding step 1", # "description": "First step of the user onboarding flow showing the project selection screen", # "screenshot_url": "https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png", # "created_at": "2024-03-15T10:22:31Z", From bcc605fe5a5f6496e9442e53a61b3e14e97fd3b5 Mon Sep 17 00:00:00 2001 From: Stephen Lumenta Date: Wed, 10 Jun 2026 14:18:53 +0200 Subject: [PATCH 3/3] fix: keep original operationId screenshot/show and regenerate doc/compiled.json Renaming an operationId changes generated client method and model names in every SDK, which is a breaking change; the repo convention is singular resource names for single-resource operations (plural only for /list). The compare-output CI job requires the regenerated bundle to ship with the spec change. Co-Authored-By: Claude Fable 5 --- doc/compiled.json | 29 +++++++++++++++++++++-------- paths/screenshots/show.yaml | 2 +- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/doc/compiled.json b/doc/compiled.json index bf43680f..ab2962f9 100644 --- a/doc/compiled.json +++ b/doc/compiled.json @@ -21237,7 +21237,7 @@ "/projects/{project_id}/screenshots/{id}": { "get": { "summary": "Get a single screenshot", - "description": "Get details on a single screenshot for a given project.", + "description": "Returns the full metadata for one screenshot in a project, identified by its string code.\n\nA screenshot in Phrase is an image file uploaded to a project to give translators visual\ncontext. Each screenshot can be linked to one or more translation keys via rectangular\nmarker regions. This endpoint retrieves the screenshot's name, description, hosted image\nURL, and the count of markers currently attached to it.\n\nUse this endpoint when you have a screenshot's `id` (its string code) and need its current\nmetadata or image URL. To discover screenshot codes, first call the list endpoint\n(`screenshots/list`); to inspect the individual markers attached to a screenshot, call the\nscreenshot markers list endpoint.\n\n**Feature requirement:** All screenshot endpoints require the Attachable Screenshots feature\nto be enabled on the account. Requests made against an account without this feature return\n`403` immediately, before any permission checks run.\n\n**Access control:** The access token must carry the `read` OAuth scope. The authenticated\nuser must have at least read permission on the project and on the screenshot resource.\n\n**The `id` field is a string code, not a numeric primary key.** The value returned in the\nresponse `id` field is the screenshot's code (e.g., `d2e056aa9e70b01121f41693e344f5ee`).\nPass this value to other screenshot endpoints such as update or delete.\n\n**Branches:** Pass the `branch` query parameter to read from a branch project. Omit it to\nread from the default branch. An invalid or inaccessible branch causes a `404` response.\n", "operationId": "screenshot/show", "tags": [ "Screenshots" @@ -21269,6 +21269,15 @@ "application/json": { "schema": { "$ref": "#/components/schemas/screenshot" + }, + "example": { + "id": "d2e056aa9e70b01121f41693e344f5ee", + "name": "Onboarding step 1", + "description": "First step of the user onboarding flow showing the project selection screen", + "screenshot_url": "https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png", + "created_at": "2024-03-15T10:22:31Z", + "updated_at": "2024-03-18T14:05:12Z", + "markers_count": 3 } } }, @@ -21285,30 +21294,34 @@ } }, "400": { - "$ref": "#/components/responses/400" + "$ref": "#/components/responses/400", + "description": "Bad request. Returned when a query parameter is malformed or cannot be parsed. Check the request URL and query parameter syntax; refer to the error `message` field for the specific parsing problem." }, "401": { - "$ref": "#/components/responses/401" + "$ref": "#/components/responses/401", + "description": "Unauthorized. The request did not include a valid access token, or the token has expired. Generate a new access token in Phrase account settings and pass it via HTTP Basic auth or the `Authorization: Bearer` header. When 2FA is enabled, supply the current OTP in `X-PhraseApp-OTP`." }, "403": { "$ref": "#/components/responses/403", - "description": "Forbidden. Returned when the access token lacks the `read` scope, when the requesting user is not allowed to read this screenshot, or when the account does not have the Attachable Screenshots feature." + "description": "Forbidden. Returned in the following cases:\n\n| Cause | Remediation |\n|-------|-------------|\n| The Attachable Screenshots feature is not enabled on the account plan. | Upgrade the account plan or contact Phrase support to enable the Attachable Screenshots feature. |\n| The access token lacks the `read` OAuth scope. | Re-authorize the OAuth application with the `read` scope. |\n| The authenticated user does not have read permission on this project or screenshot. | Ask a project admin to grant at least Viewer access to this project. |\n" }, "404": { - "$ref": "#/components/responses/404" + "$ref": "#/components/responses/404", + "description": "Not found. Returned when the project, screenshot, or the specified branch does not exist or is not accessible to the requesting user. Verify the `project_id`, `id`, and `branch` values. Use the screenshots list endpoint to retrieve valid screenshot codes for the project." }, "429": { - "$ref": "#/components/responses/429" + "$ref": "#/components/responses/429", + "description": "Rate limit exceeded. The account has exceeded its API request quota for the current window. Reduce request frequency and implement exponential backoff. Check the `X-Rate-Limit-Reset` response header for the Unix timestamp when the quota resets." } }, "x-code-samples": [ { "lang": "Curl", - "source": "curl \"https://api.phrase.com/v2/projects/:project_id/screenshots/:id?branch=my-feature-branch\" \\\n -u USERNAME_OR_ACCESS_TOKEN" + "source": "curl \"https://api.phrase.com/v2/projects/3d7ce831a3e9e1b843dc16d36ee5b9f4/screenshots/d2e056aa9e70b01121f41693e344f5ee\" \\\n -u USERNAME_OR_ACCESS_TOKEN\n\n# Expected response (HTTP 200):\n# {\n# \"id\": \"d2e056aa9e70b01121f41693e344f5ee\",\n# \"name\": \"Onboarding step 1\",\n# \"description\": \"First step of the user onboarding flow showing the project selection screen\",\n# \"screenshot_url\": \"https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png\",\n# \"created_at\": \"2024-03-15T10:22:31Z\",\n# \"updated_at\": \"2024-03-18T14:05:12Z\",\n# \"markers_count\": 3\n# }" }, { "lang": "CLI v2", - "source": "phrase screenshots show \\\n--project_id \\\n--id \\\n--branch my-feature-branch \\\n--access_token " + "source": "phrase screenshots show \\\n--project_id 3d7ce831a3e9e1b843dc16d36ee5b9f4 \\\n--id d2e056aa9e70b01121f41693e344f5ee \\\n--access_token 4a8e3f7c2b1d9e6a0c5f8b2d4e7a1c3f\n\n# Expected response (HTTP 200):\n# {\n# \"id\": \"d2e056aa9e70b01121f41693e344f5ee\",\n# \"name\": \"Onboarding step 1\",\n# \"description\": \"First step of the user onboarding flow showing the project selection screen\",\n# \"screenshot_url\": \"https://assets.phrase.com/screenshots/d2e056aa9e70b01121f41693e344f5ee.png\",\n# \"created_at\": \"2024-03-15T10:22:31Z\",\n# \"updated_at\": \"2024-03-18T14:05:12Z\",\n# \"markers_count\": 3\n# }" } ], "x-cli-version": "2.5" diff --git a/paths/screenshots/show.yaml b/paths/screenshots/show.yaml index 454b4dc0..6c984be1 100644 --- a/paths/screenshots/show.yaml +++ b/paths/screenshots/show.yaml @@ -26,7 +26,7 @@ description: | **Branches:** Pass the `branch` query parameter to read from a branch project. Omit it to read from the default branch. An invalid or inaccessible branch causes a `404` response. -operationId: screenshots/show +operationId: screenshot/show tags: - Screenshots parameters: