From 530fe41be51a0d8c2133c338012f893c7c3d87e3 Mon Sep 17 00:00:00 2001 From: Stephen Lumenta Date: Mon, 15 Jun 2026 14:10:31 +0200 Subject: [PATCH 1/2] feat(API): improve delete /projects/{project_id}/keys/{id}/key_links/{child_key_id} documentation --- doc/compiled.json | 90 ++++++++++++++++++++++++++++++++---- paths/key_links/destroy.yaml | 85 +++++++++++++++++++++++++++++++--- 2 files changed, 161 insertions(+), 14 deletions(-) diff --git a/doc/compiled.json b/doc/compiled.json index afba5090..bf713e6e 100644 --- a/doc/compiled.json +++ b/doc/compiled.json @@ -31020,7 +31020,7 @@ "/projects/{project_id}/keys/{id}/key_links/{child_key_id}": { "delete": { "summary": "Unlink a child key from a parent key", - "description": "Unlinks a single child key from a given parent key.", + "description": "Removes a single child key from a parent key's link group. A link group is the relationship model that keeps child keys synchronized with a parent: while linked, a child key's translations are derived from the parent's content. When you call this endpoint, the child key leaves the group and becomes independent — its existing translations are updated with the parent's current content and then marked unverified, signalling that reviewers should confirm the content is still appropriate for the child's context.\n\nUse this endpoint when you need to detach one specific child key while keeping other children linked. To detach multiple children at once, use the batch unlink endpoint.\n\nThis operation requires write permission on translation key links and is restricted to main projects. Calling it on a branch returns 403. Returns 422 when the specified child key is not linked to this parent key.\n\nWhen the operation fails, the response body includes an errors array. Each error object carries a code field using UPPER_SNAKE_CASE stable identifiers (for example, KEY_IS_NOT_LINKED) that you can match programmatically to distinguish error conditions.\n", "operationId": "key_links/destroy", "tags": [ "Linked Keys" @@ -31038,37 +31038,111 @@ { "in": "path", "name": "child_key_id", + "description": "The ID of the child translation key to unlink from the parent.", "required": true, - "description": "The ID of the child key to unlink.", "schema": { - "type": "string" + "type": "string", + "example": "1234abcd1234cdef1234abcd1234cdef" } } ], "responses": { "200": { - "description": "OK" + "description": "The child key was successfully unlinked from the parent key. The response body is empty.", + "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" + } + }, + "content": {} }, "400": { + "description": "Bad request. Returned when the request is malformed, such as unparseable JSON in the request body. Fix the request format and retry.\n", "$ref": "#/components/responses/400" }, "401": { + "description": "Unauthorized. The access token is missing or invalid. Provide a valid token with the write scope and retry.\n", "$ref": "#/components/responses/401" }, "403": { - "$ref": "#/components/responses/403", - "description": "Forbidden. Returned when the access token lacks the `write` scope or when the requesting user is not allowed to unlink this key." + "description": "Forbidden. Returned when the access token lacks the write scope, when the requester does not have write permission on translation key links, or when the project is a branch rather than a main project. Verify scope and project type before retrying.\n", + "$ref": "#/components/responses/403" }, "404": { + "description": "Not found. Returned when the parent key does not exist in the project. Check that the project_id and id path parameters reference existing resources.\n", "$ref": "#/components/responses/404" }, "422": { - "$ref": "#/components/responses/422" + "description": "Unprocessable entity. Returned when the child key is not currently linked to the specified parent key, or when a translation update fails during the unlink process. The errors array contains a code field with a stable UPPER_SNAKE_CASE identifier — KEY_IS_NOT_LINKED indicates the child is not in this key link group. Verify the child_key_id is linked to this parent before retrying.\n", + "content": { + "application/json": { + "schema": { + "type": "object", + "title": "key_links/destroy/error", + "properties": { + "message": { + "type": "string", + "description": "A human-readable description of the error." + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "title": "key_links/destroy/error/item", + "properties": { + "resource": { + "type": "string", + "description": "The resource type the error applies to." + }, + "field": { + "type": "string", + "description": "The field the error applies to." + }, + "message": { + "type": "string", + "description": "A human-readable description of the validation error." + }, + "code": { + "type": "string", + "description": "A stable UPPER_SNAKE_CASE machine-readable code identifying this error condition.", + "example": "KEY_IS_NOT_LINKED" + } + } + } + } + } + }, + "example": { + "message": "Child key 1234abcd1234cdef1234abcd1234cdef is not linked to this parent key.", + "errors": [ + { + "resource": "base", + "field": "base", + "message": "Child key 1234abcd1234cdef1234abcd1234cdef is not linked to this parent key.", + "code": "KEY_IS_NOT_LINKED" + } + ] + } + } + } }, "429": { + "description": "Too many requests. You have exceeded the rate limit. Wait until the time indicated in X-Rate-Limit-Reset before retrying.\n", "$ref": "#/components/responses/429" } - } + }, + "x-code-samples": [ + { + "lang": "Curl", + "source": "curl -i \"https://api.phrase.com/v2/projects/:project_id/keys/:id/key_links/1234abcd1234cdef1234abcd1234cdef\" \\\n -u USERNAME_OR_ACCESS_TOKEN \\\n -X DELETE\n\n# Expected response on success:\n# HTTP/2 200\n# x-rate-limit-limit: 1000\n# x-rate-limit-remaining: 999\n# x-rate-limit-reset: 1705000000\n# (empty body)" + } + ] } }, "/accounts/{account_id}/automations": { diff --git a/paths/key_links/destroy.yaml b/paths/key_links/destroy.yaml index 251c2a43..3dcc3867 100644 --- a/paths/key_links/destroy.yaml +++ b/paths/key_links/destroy.yaml @@ -1,6 +1,13 @@ --- summary: Unlink a child key from a parent key -description: Unlinks a single child key from a given parent key. +description: | + Removes a single child key from a parent key's link group. A link group is the relationship model that keeps child keys synchronized with a parent: while linked, a child key's translations are derived from the parent's content. When you call this endpoint, the child key leaves the group and becomes independent — its existing translations are updated with the parent's current content and then marked unverified, signalling that reviewers should confirm the content is still appropriate for the child's context. + + Use this endpoint when you need to detach one specific child key while keeping other children linked. To detach multiple children at once, use the batch unlink endpoint. + + This operation requires write permission on translation key links and is restricted to main projects. Calling it on a branch returns 403. Returns 422 when the specified child key is not linked to this parent key. + + When the operation fails, the response body includes an errors array. Each error object carries a code field using UPPER_SNAKE_CASE stable identifiers (for example, KEY_IS_NOT_LINKED) that you can match programmatically to distinguish error conditions. operationId: key_links/destroy tags: - Linked Keys @@ -10,24 +17,90 @@ parameters: - "$ref": "../../parameters.yaml#/key_id_as_id" - in: path name: child_key_id + description: The ID of the child translation key to unlink from the parent. required: true - description: The ID of the child key to unlink. schema: type: string - + example: 1234abcd1234cdef1234abcd1234cdef responses: '200': - description: OK + description: The child key was successfully unlinked from the parent key. The response body is empty. + 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" + content: {} '400': + description: | + Bad request. Returned when the request is malformed, such as unparseable JSON in the request body. Fix the request format and retry. "$ref": "../../responses.yaml#/400" '401': + description: | + Unauthorized. The access token is missing or invalid. Provide a valid token with the write scope and retry. "$ref": "../../responses.yaml#/401" '403': + description: | + Forbidden. Returned when the access token lacks the write scope, when the requester does not have write permission on translation key links, or when the project is a branch rather than a main project. Verify scope and project type before retrying. "$ref": "../../responses.yaml#/403" - description: Forbidden. Returned when the access token lacks the `write` scope or when the requesting user is not allowed to unlink this key. '404': + description: | + Not found. Returned when the parent key does not exist in the project. Check that the project_id and id path parameters reference existing resources. "$ref": "../../responses.yaml#/404" '422': - "$ref": "../../responses.yaml#/422" + description: | + Unprocessable entity. Returned when the child key is not currently linked to the specified parent key, or when a translation update fails during the unlink process. The errors array contains a code field with a stable UPPER_SNAKE_CASE identifier — KEY_IS_NOT_LINKED indicates the child is not in this key link group. Verify the child_key_id is linked to this parent before retrying. + content: + application/json: + schema: + type: object + title: key_links/destroy/error + properties: + message: + type: string + description: A human-readable description of the error. + errors: + type: array + items: + type: object + title: key_links/destroy/error/item + properties: + resource: + type: string + description: The resource type the error applies to. + field: + type: string + description: The field the error applies to. + message: + type: string + description: A human-readable description of the validation error. + code: + type: string + description: A stable UPPER_SNAKE_CASE machine-readable code identifying this error condition. + example: KEY_IS_NOT_LINKED + example: + message: Child key 1234abcd1234cdef1234abcd1234cdef is not linked to this parent key. + errors: + - resource: base + field: base + message: Child key 1234abcd1234cdef1234abcd1234cdef is not linked to this parent key. + code: KEY_IS_NOT_LINKED '429': + description: | + Too many requests. You have exceeded the rate limit. Wait until the time indicated in X-Rate-Limit-Reset before retrying. "$ref": "../../responses.yaml#/429" +x-code-samples: +- lang: Curl + source: |- + curl -i "https://api.phrase.com/v2/projects/:project_id/keys/:id/key_links/1234abcd1234cdef1234abcd1234cdef" \ + -u USERNAME_OR_ACCESS_TOKEN \ + -X DELETE + + # Expected response on success: + # HTTP/2 200 + # x-rate-limit-limit: 1000 + # x-rate-limit-remaining: 999 + # x-rate-limit-reset: 1705000000 + # (empty body) From 6ae75921033d38d33722134f1ef4b653607daa45 Mon Sep 17 00:00:00 2001 From: Stephen Lumenta Date: Wed, 17 Jun 2026 21:15:59 +0100 Subject: [PATCH 2/2] fix(API): clean up delete .../key_links/{child_key_id} Apply the batch review conventions: - Trim the scope/status sentence from the description (write-permission, 'returns 403 on a branch', 'returns 422 when ...'); the 403 and 422 responses already document these. Keep the main-project restriction and the error-code-format note. - Drop the '# Expected response on success: ...' trailer from the curl sample so it shows only the request. Co-Authored-By: Claude Opus 4.8 (1M context) --- doc/compiled.json | 4 ++-- paths/key_links/destroy.yaml | 11 +---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/doc/compiled.json b/doc/compiled.json index bf713e6e..f1157187 100644 --- a/doc/compiled.json +++ b/doc/compiled.json @@ -31020,7 +31020,7 @@ "/projects/{project_id}/keys/{id}/key_links/{child_key_id}": { "delete": { "summary": "Unlink a child key from a parent key", - "description": "Removes a single child key from a parent key's link group. A link group is the relationship model that keeps child keys synchronized with a parent: while linked, a child key's translations are derived from the parent's content. When you call this endpoint, the child key leaves the group and becomes independent — its existing translations are updated with the parent's current content and then marked unverified, signalling that reviewers should confirm the content is still appropriate for the child's context.\n\nUse this endpoint when you need to detach one specific child key while keeping other children linked. To detach multiple children at once, use the batch unlink endpoint.\n\nThis operation requires write permission on translation key links and is restricted to main projects. Calling it on a branch returns 403. Returns 422 when the specified child key is not linked to this parent key.\n\nWhen the operation fails, the response body includes an errors array. Each error object carries a code field using UPPER_SNAKE_CASE stable identifiers (for example, KEY_IS_NOT_LINKED) that you can match programmatically to distinguish error conditions.\n", + "description": "Removes a single child key from a parent key's link group. A link group is the relationship model that keeps child keys synchronized with a parent: while linked, a child key's translations are derived from the parent's content. When you call this endpoint, the child key leaves the group and becomes independent — its existing translations are updated with the parent's current content and then marked unverified, signalling that reviewers should confirm the content is still appropriate for the child's context.\n\nUse this endpoint when you need to detach one specific child key while keeping other children linked. To detach multiple children at once, use the batch unlink endpoint. This operation is only available on main projects.\n\nWhen the operation fails, the response body includes an errors array. Each error object carries a code field using UPPER_SNAKE_CASE stable identifiers (for example, KEY_IS_NOT_LINKED) that you can match programmatically to distinguish error conditions.\n", "operationId": "key_links/destroy", "tags": [ "Linked Keys" @@ -31140,7 +31140,7 @@ "x-code-samples": [ { "lang": "Curl", - "source": "curl -i \"https://api.phrase.com/v2/projects/:project_id/keys/:id/key_links/1234abcd1234cdef1234abcd1234cdef\" \\\n -u USERNAME_OR_ACCESS_TOKEN \\\n -X DELETE\n\n# Expected response on success:\n# HTTP/2 200\n# x-rate-limit-limit: 1000\n# x-rate-limit-remaining: 999\n# x-rate-limit-reset: 1705000000\n# (empty body)" + "source": "curl -i \"https://api.phrase.com/v2/projects/:project_id/keys/:id/key_links/1234abcd1234cdef1234abcd1234cdef\" \\\n -u USERNAME_OR_ACCESS_TOKEN \\\n -X DELETE" } ] } diff --git a/paths/key_links/destroy.yaml b/paths/key_links/destroy.yaml index 3dcc3867..0967335b 100644 --- a/paths/key_links/destroy.yaml +++ b/paths/key_links/destroy.yaml @@ -3,9 +3,7 @@ summary: Unlink a child key from a parent key description: | Removes a single child key from a parent key's link group. A link group is the relationship model that keeps child keys synchronized with a parent: while linked, a child key's translations are derived from the parent's content. When you call this endpoint, the child key leaves the group and becomes independent — its existing translations are updated with the parent's current content and then marked unverified, signalling that reviewers should confirm the content is still appropriate for the child's context. - Use this endpoint when you need to detach one specific child key while keeping other children linked. To detach multiple children at once, use the batch unlink endpoint. - - This operation requires write permission on translation key links and is restricted to main projects. Calling it on a branch returns 403. Returns 422 when the specified child key is not linked to this parent key. + Use this endpoint when you need to detach one specific child key while keeping other children linked. To detach multiple children at once, use the batch unlink endpoint. This operation is only available on main projects. When the operation fails, the response body includes an errors array. Each error object carries a code field using UPPER_SNAKE_CASE stable identifiers (for example, KEY_IS_NOT_LINKED) that you can match programmatically to distinguish error conditions. operationId: key_links/destroy @@ -97,10 +95,3 @@ x-code-samples: curl -i "https://api.phrase.com/v2/projects/:project_id/keys/:id/key_links/1234abcd1234cdef1234abcd1234cdef" \ -u USERNAME_OR_ACCESS_TOKEN \ -X DELETE - - # Expected response on success: - # HTTP/2 200 - # x-rate-limit-limit: 1000 - # x-rate-limit-remaining: 999 - # x-rate-limit-reset: 1705000000 - # (empty body)