From 2b72e621af2124f307071f3925bb2706fe42be4f Mon Sep 17 00:00:00 2001 From: Brianna Delgado Date: Mon, 10 Aug 2026 15:16:58 -0400 Subject: [PATCH 1/3] feat: Document translation memory management endpoints Add API reference pages for retrieving, listing segments, importing, exporting, deleting translation memories, and polling import/export jobs. Updates the OpenAPI spec, navigation, permission scopes, and release notes to match. Co-Authored-By: Claude Opus 5 --- api-reference/openapi.json | 1120 ++++++++++++++++- api-reference/openapi.yaml | 844 ++++++++++++- .../delete-a-translation-memory.mdx | 13 + .../export-a-translation-memory.mdx | 74 ++ .../import-a-translation-memory.mdx | 102 ++ .../list-translation-memory-segments.mdx | 29 + .../retrieve-a-translation-memory-job.mdx | 33 + .../retrieve-a-translation-memory.mdx | 9 + docs.json | 8 +- docs/admin/permission-scopes.mdx | 15 +- docs/resources/roadmap-and-release-notes.mdx | 12 +- 11 files changed, 2212 insertions(+), 47 deletions(-) create mode 100644 api-reference/translation-memory/delete-a-translation-memory.mdx create mode 100644 api-reference/translation-memory/export-a-translation-memory.mdx create mode 100644 api-reference/translation-memory/import-a-translation-memory.mdx create mode 100644 api-reference/translation-memory/list-translation-memory-segments.mdx create mode 100644 api-reference/translation-memory/retrieve-a-translation-memory-job.mdx create mode 100644 api-reference/translation-memory/retrieve-a-translation-memory.mdx diff --git a/api-reference/openapi.json b/api-reference/openapi.json index c2827fbc..4df9168c 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -63,7 +63,7 @@ }, { "name": "TranslationMemories", - "description": "The translation memory endpoints allow you to interact with your account's translation memories, used to store\nand reuse previously created translations. Translation memories can be used in text translation requests by\nspecifying the `translation_memory_id` parameter to denote a specific translation memory and the\n`translation_memory_threshold` which defines the minimum matching percentage required for a translation memory\nsegment to be applied (recommended to be 75% or higher)." + "description": "The translation memory endpoints allow you to manage your account's translation memories, used to store\nand reuse previously created translations. You can list and retrieve translation memories, page through\ntheir stored segments, create one by importing a TMX file, export one back to TMX, and delete one.\nEditing the contents of an existing translation memory is not supported; import a new one instead.\n\nImporting and exporting run as background jobs. Create the job, then poll\n`GET /v3/translation_memories/jobs/{job_id}` until it reports `completed`.\n\nTranslation memories can be used in text translation requests by\nspecifying the `translation_memory_id` parameter to denote a specific translation memory and the\n`translation_memory_threshold` which defines the minimum matching percentage required for a translation memory\nsegment to be applied (recommended to be 75% or higher). A translation request fails with `404` if the\ntranslation memory does not exist or does not cover the requested language pair." }, { "name": "VoiceAPI", @@ -4278,14 +4278,782 @@ "type": "integer", "default": 10, "minimum": 1, - "maximum": 25 + "maximum": 100 + }, + "description": "The maximum number of translation memories to return. Values above 100 are reduced to 100." + } + ], + "responses": { + "200": { + "description": "Returns a list of translation memories.", + "headers": { + "X-Trace-ID": { + "$ref": "#/components/headers/X-Trace-ID" + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "translation_memories": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TranslationMemory" + } + }, + "total_count": { + "type": "integer", + "description": "The total number of translation memories available.", + "example": 2 + } + } + }, + "examples": { + "Basic": { + "value": { + "translation_memories": [ + { + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994", + "name": "Legal", + "source_language": "en", + "target_languages": [ + "es", + "de" + ], + "segment_count": 3542 + }, + { + "translation_memory_id": "855d36ab-6112-4ef5-8868-0d69bc4d826a", + "name": "Medical terms", + "source_language": "de", + "target_languages": [ + "fr", + "ja", + "zh" + ], + "segment_count": 23 + } + ], + "total_count": 2 + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/ForbiddenScoped" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "503": { + "$ref": "#/components/responses/ServiceUnavailable" + } + }, + "security": [ + { + "auth_header": [] + } + ] + } + }, + "/v3/translation_memories/import": { + "post": { + "tags": [ + "TranslationMemories" + ], + "summary": "Import a translation memory", + "operationId": "createTranslationMemoryImport", + "description": "Create a translation memory by importing a TMX file.\n\nThis endpoint does not accept the file itself. It returns a short-lived `upload_url`, and you\nupload the TMX file to that URL with a separate `PUT` request. Processing starts automatically\nonce the upload completes, so there is no third call to confirm it.\n\n1. `POST /v3/translation_memories/import` with the file's name and size. The response returns a\n `job_id`, an `upload_url`, and the `expires_at` time after which the URL stops working.\n2. `PUT` the TMX file to `upload_url`. Do not send your `Authorization` header on this request,\n because the URL is already signed.\n3. Poll [Retrieve an import or export job](/api-reference/translation-memory/retrieve-a-translation-memory-job)\n until the status is `completed`. The new `translation_memory_id` is on the job result.\n\nRequires an API key with the `translation_memories:write` scope.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "source_file" + ], + "properties": { + "source_file": { + "type": "object", + "required": [ + "file_name", + "content_length" + ], + "properties": { + "file_name": { + "description": "The name of the TMX file you intend to upload, including its extension. At most 100\ncharacters.", + "type": "string", + "maxLength": 100, + "example": "legal.tmx" + }, + "content_type": { + "description": "The MIME type of the file. Defaults to `application/xml`, which is correct for TMX.\nTMX is currently the only supported import format. At most 127 characters.", + "type": "string", + "default": "application/xml", + "maxLength": 127, + "example": "application/xml" + }, + "content_length": { + "description": "The size of the file in bytes. Must be greater than 0 and within DeepL's size limit for\nTMX imports. An oversize value is rejected with `400 Bad Request`.", + "type": "integer", + "format": "int64", + "minimum": 1, + "example": 1024 + } + } + }, + "parameters": { + "type": "object", + "properties": { + "display_name": { + "description": "The name to give the new translation memory. If you omit this, the translation memory\nis created without a name.", + "type": "string", + "example": "Legal" + } + } + } + } + }, + "examples": { + "Basic": { + "value": { + "source_file": { + "file_name": "legal.tmx", + "content_type": "application/xml", + "content_length": 1024 + }, + "parameters": { + "display_name": "Legal" + } + } + } + } + } + } + }, + "responses": { + "202": { + "description": "The import job was created. Upload the file to `upload_url` before `expires_at`.", + "headers": { + "X-Trace-ID": { + "$ref": "#/components/headers/X-Trace-ID" + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "job_id", + "upload_url", + "expires_at" + ], + "properties": { + "job_id": { + "description": "The identifier of the import job. Use it to poll the job's status.", + "type": "string", + "example": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" + }, + "upload_url": { + "description": "A signed URL to `PUT` the TMX file to. Do not send your `Authorization` header with the\nupload request.", + "type": "string", + "format": "uri", + "example": "https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" + }, + "expires_at": { + "description": "The time the `upload_url` stops accepting uploads, in the ISO 8601-1:2019 format. After\nthis time, create a new import job.", + "type": "string", + "format": "date-time", + "example": "2026-08-06T15:34:25.223Z" + } + } + }, + "examples": { + "Basic": { + "value": { + "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "upload_url": "https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "expires_at": "2026-08-06T15:34:25.223Z" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/ForbiddenScoped" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "456": { + "description": "You have reached the maximum number of translation memories for your account. Delete a\ntranslation memory before creating another one.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "503": { + "$ref": "#/components/responses/ServiceUnavailable" + } + }, + "security": [ + { + "auth_header": [] + } + ] + } + }, + "/v3/translation_memories/jobs/{job_id}": { + "get": { + "tags": [ + "TranslationMemories" + ], + "summary": "Retrieve an import or export job", + "operationId": "getTranslationMemoryJob", + "description": "Retrieve the status of a translation memory import or export job.\n\nPoll this endpoint after creating a job. The `operation` field tells you which kind of job it is,\nand the single entry in `results` carries the status and, once the job finishes, its output: the\nnew `translation_memory_id` for an import, or a `download_url` for an export.\n\nRequires an API key with the `translation_memories:read` scope.", + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The identifier of the job, returned when you created the import or export.", + "example": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" + } + ], + "responses": { + "200": { + "description": "Returns the current state of the job.", + "headers": { + "X-Trace-ID": { + "$ref": "#/components/headers/X-Trace-ID" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TranslationMemoryJob" + }, + "examples": { + "Import awaiting upload": { + "value": { + "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "product": "translation_memory", + "operation": "import", + "created_at": "2026-08-06T15:04:25.223Z", + "updated_at": "2026-08-06T15:04:25.223Z", + "source_file": { + "content_type": "application/xml", + "content_length": 1024 + }, + "parameters": { + "display_name": "Legal" + }, + "results": [ + { + "status": "awaiting_input", + "status_metadata": { + "required_action": "Waiting for upload" + } + } + ] + } + }, + "Import completed": { + "value": { + "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "product": "translation_memory", + "operation": "import", + "created_at": "2026-08-06T15:04:25.223Z", + "updated_at": "2026-08-06T15:06:11.418Z", + "source_file": { + "content_type": "application/xml", + "content_length": 1024 + }, + "parameters": { + "display_name": "Legal" + }, + "results": [ + { + "status": "completed", + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994", + "skipped_segment_count": 12 + } + ] + } + }, + "Export completed": { + "value": { + "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", + "product": "translation_memory", + "operation": "export", + "created_at": "2026-08-06T15:04:25.223Z", + "updated_at": "2026-08-06T15:05:02.771Z", + "parameters": { + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" + }, + "results": [ + { + "status": "completed", + "download_url": "https://assets.deepl.com/download/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", + "expires_at": "2026-08-06T16:05:02.771Z" + } + ] + } + }, + "Failed": { + "value": { + "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "product": "translation_memory", + "operation": "import", + "created_at": "2026-08-06T15:04:25.223Z", + "updated_at": "2026-08-06T15:04:58.102Z", + "source_file": { + "content_type": "application/xml", + "content_length": 1024 + }, + "parameters": { + "display_name": "Legal" + }, + "results": [ + { + "status": "failed", + "error": { + "message": "The uploaded file is not valid TMX." + } + } + ] + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/ForbiddenScoped" + }, + "404": { + "description": "The job could not be found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "503": { + "$ref": "#/components/responses/ServiceUnavailable" + } + }, + "security": [ + { + "auth_header": [] + } + ] + } + }, + "/v3/translation_memories/{translation_memory_id}": { + "get": { + "tags": [ + "TranslationMemories" + ], + "summary": "Retrieve a translation memory", + "operationId": "getTranslationMemory", + "description": "Retrieve a single translation memory by its ID, including its languages and segment count.\n\nTo read the stored segments themselves, use\n[List translation memory segments](/api-reference/translation-memory/list-translation-memory-segments).\n\nRequires an API key with the `translation_memories:read` scope.", + "parameters": [ + { + "name": "translation_memory_id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/TranslationMemoryId" + }, + "description": "The ID of the translation memory to retrieve." + } + ], + "responses": { + "200": { + "description": "Returns the translation memory.", + "headers": { + "X-Trace-ID": { + "$ref": "#/components/headers/X-Trace-ID" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TranslationMemory" + }, + "examples": { + "Basic": { + "value": { + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994", + "name": "Legal", + "source_language": "en", + "target_languages": [ + "es", + "de" + ], + "segment_count": 3542, + "creation_time": "2026-04-01T16:34:25.223Z", + "updated_time": "2026-08-06T09:12:44.108Z" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/ForbiddenScoped" + }, + "404": { + "description": "The translation memory could not be found. This is also returned for a translation memory that\nbelongs to another account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "503": { + "$ref": "#/components/responses/ServiceUnavailable" + } + }, + "security": [ + { + "auth_header": [] + } + ] + }, + "delete": { + "tags": [ + "TranslationMemories" + ], + "summary": "Delete a translation memory", + "operationId": "deleteTranslationMemory", + "description": "Delete a translation memory and all of its segments.\n\nDeletion is permanent and cannot be undone. Export the translation memory first if you need a\ncopy of its contents. Translation requests that pass the deleted `translation_memory_id` fail\nafter this call, so update your integration before deleting.\n\nRequires an API key with the `translation_memories:write` scope.", + "parameters": [ + { + "name": "translation_memory_id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/TranslationMemoryId" + }, + "description": "The ID of the translation memory to delete." + } + ], + "responses": { + "204": { + "description": "The translation memory was deleted. The response has no body.", + "headers": { + "X-Trace-ID": { + "$ref": "#/components/headers/X-Trace-ID" + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/ForbiddenScoped" + }, + "404": { + "description": "The translation memory could not be found. This is also returned for a translation memory that\nbelongs to another account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "503": { + "$ref": "#/components/responses/ServiceUnavailable" + } + }, + "security": [ + { + "auth_header": [] + } + ] + } + }, + "/v3/translation_memories/{translation_memory_id}/segments": { + "get": { + "tags": [ + "TranslationMemories" + ], + "summary": "List translation memory segments", + "operationId": "getTranslationMemorySegments", + "description": "List the segments stored in a translation memory, one page at a time. Each entry is a source\nsegment with its translations in every target language.\n\nThis endpoint uses cursor-based pagination rather than the numbered pages used by\n[List translation memories](/api-reference/translation-memory/list-translation-memories). Omit\n`page_cursor` on your first call, then pass the `next_page_cursor` from each response to get the\nfollowing page. When a response has no `next_page_cursor`, you have reached the last page.\n\nRequires an API key with the `translation_memories:read` scope.", + "parameters": [ + { + "name": "translation_memory_id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/TranslationMemoryId" + }, + "description": "The ID of the translation memory whose segments you want to list." + }, + { + "name": "page_size", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 100 + }, + "description": "The maximum number of segments to return per page. Values above 100 are reduced to 100." + }, + { + "name": "page_cursor", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The `next_page_cursor` from a previous response. Omit this on your first call. Treat the value\nas opaque: do not build or modify it yourself." + }, + { + "name": "filter_text", + "in": "query", + "schema": { + "type": "string", + "minLength": 2 + }, + "description": "Return only segments where this text appears in the source or in any target translation. Must be\nat least 2 characters." + }, + { + "name": "filter_case_sensitive", + "in": "query", + "schema": { + "type": "boolean", + "default": false + }, + "description": "Whether `filter_text` is matched case-sensitively." + } + ], + "responses": { + "200": { + "description": "Returns a page of segments.", + "headers": { + "X-Trace-ID": { + "$ref": "#/components/headers/X-Trace-ID" + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "segments", + "segment_count" + ], + "properties": { + "segments": { + "description": "The segments on this page.", + "type": "array", + "items": { + "$ref": "#/components/schemas/TranslationMemorySegment" + } + }, + "segment_count": { + "description": "The total number of segments in the translation memory. This is not reduced by\n`filter_text`, so a filtered request still reports the full count. Do not use it to\npredict how many results a filtered query returns.", + "type": "integer", + "example": 3542 + }, + "next_page_cursor": { + "description": "The cursor to pass as `page_cursor` to retrieve the next page. Absent on the last page.", + "type": "string", + "example": "eyJvZmZzZXQiOjUwfQ" + } + } + }, + "examples": { + "Basic": { + "value": { + "segments": [ + { + "source_segment_id": "4f1c2d3e-8a9b-4c5d-9e6f-7a8b9c0d1e2f", + "source_text": "This agreement is governed by the laws of Germany.", + "created_time": "2026-04-01T16:34:25.223Z", + "updated_time": "2026-04-01T16:34:25.223Z", + "last_used_time": "2026-08-05T11:02:18.771Z", + "targets": [ + { + "target_segment_id": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "target_language": "de", + "target_text": "Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland.", + "created_time": "2026-04-01T16:34:25.223Z", + "updated_time": "2026-04-01T16:34:25.223Z", + "last_used_time": "2026-08-05T11:02:18.771Z" + }, + { + "target_segment_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f", + "target_language": "es", + "target_text": "Este contrato se rige por las leyes de Alemania.", + "created_time": "2026-04-01T16:34:25.223Z", + "updated_time": "2026-04-01T16:34:25.223Z", + "last_used_time": "2026-07-22T08:41:05.330Z" + } + ] + } + ], + "segment_count": 3542, + "next_page_cursor": "eyJvZmZzZXQiOjUwfQ" + } + }, + "Last page": { + "value": { + "segments": [ + { + "source_segment_id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d", + "source_text": "Termination requires 30 days written notice.", + "created_time": "2026-04-01T16:34:25.223Z", + "updated_time": "2026-04-01T16:34:25.223Z", + "last_used_time": "2026-08-01T14:20:09.512Z", + "targets": [ + { + "target_segment_id": "0d1e2f3a-4b5c-4d6e-8f7a-8b9c0d1e2f3a", + "target_language": "de", + "target_text": "Die Kündigung erfordert eine schriftliche Frist von 30 Tagen.", + "created_time": "2026-04-01T16:34:25.223Z", + "updated_time": "2026-04-01T16:34:25.223Z", + "last_used_time": "2026-08-01T14:20:09.512Z" + } + ] + } + ], + "segment_count": 3542 + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/ForbiddenScoped" + }, + "404": { + "description": "The translation memory could not be found. This is also returned for a translation memory that\nbelongs to another account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + }, + "503": { + "$ref": "#/components/responses/ServiceUnavailable" + } + }, + "security": [ + { + "auth_header": [] + } + ] + } + }, + "/v3/translation_memories/{translation_memory_id}/export": { + "post": { + "tags": [ + "TranslationMemories" + ], + "summary": "Export a translation memory", + "operationId": "createTranslationMemoryExport", + "description": "Export a translation memory as a TMX file.\n\nThe export runs as a background job. Poll\n[Retrieve an import or export job](/api-reference/translation-memory/retrieve-a-translation-memory-job)\nuntil the status is `completed`, then download the file from the `download_url` on the job result.\nThat URL is short-lived, so download the file rather than storing the link.\n\nIf a recent export of the same translation memory is still available, DeepL reuses it and returns\n`200 OK` with that job instead of starting a new one. Handle both `200` and `202` as success. If a\ndifferent export of the same translation memory is still running, the request returns\n`409 Conflict`.\n\nRequires an API key with the `translation_memories:read` scope.", + "parameters": [ + { + "name": "translation_memory_id", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/TranslationMemoryId" }, - "description": "The maximum number of translation memories to return." + "description": "The ID of the translation memory to export." } ], "responses": { "200": { - "description": "Returns a list of translation memories.", + "description": "An existing export was reused. Poll the returned `job_id` for the download URL.", "headers": { "X-Trace-ID": { "$ref": "#/components/headers/X-Trace-ID" @@ -4294,48 +5062,40 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "translation_memories": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TranslationMemory" + "$ref": "#/components/schemas/CreateTranslationMemoryExport" + }, + "examples": { + "Basic": { + "value": { + "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", + "parameters": { + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" } - }, - "total_count": { - "type": "integer", - "description": "The total number of translation memories available.", - "example": 2 } } + } + } + } + }, + "202": { + "description": "The export job was created. Poll the returned `job_id` for the download URL.", + "headers": { + "X-Trace-ID": { + "$ref": "#/components/headers/X-Trace-ID" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTranslationMemoryExport" }, "examples": { "Basic": { "value": { - "translation_memories": [ - { - "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994", - "name": "Legal", - "source_language": "en", - "target_languages": [ - "es", - "de" - ], - "segment_count": 3542 - }, - { - "translation_memory_id": "855d36ab-6112-4ef5-8868-0d69bc4d826a", - "name": "Medical terms", - "source_language": "de", - "target_languages": [ - "fr", - "ja", - "zh" - ], - "segment_count": 23 - } - ], - "total_count": 2 + "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", + "parameters": { + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" + } } } } @@ -4351,6 +5111,26 @@ "403": { "$ref": "#/components/responses/ForbiddenScoped" }, + "404": { + "description": "The translation memory could not be found. This is also returned for a translation memory that\nbelongs to another account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "409": { + "description": "An export of this translation memory is already in progress. Poll that job instead of starting\nanother export.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, "429": { "$ref": "#/components/responses/TooManyRequests" }, @@ -8583,6 +9363,268 @@ ], "example": "es" }, + "TranslationMemorySegment": { + "description": "A source segment stored in a translation memory, with its translations.", + "type": "object", + "required": [ + "source_segment_id", + "source_text", + "targets" + ], + "properties": { + "source_segment_id": { + "description": "A unique ID assigned to the source segment.", + "type": "string", + "example": "4f1c2d3e-8a9b-4c5d-9e6f-7a8b9c0d1e2f" + }, + "source_text": { + "description": "The source text of the segment.", + "type": "string", + "example": "This agreement is governed by the laws of Germany." + }, + "created_time": { + "description": "The time the segment was added, in the ISO 8601-1:2019 format.", + "type": "string", + "format": "date-time", + "example": "2026-04-01T16:34:25.223Z" + }, + "updated_time": { + "description": "The time the segment was last changed, in the ISO 8601-1:2019 format.", + "type": "string", + "format": "date-time", + "example": "2026-04-01T16:34:25.223Z" + }, + "last_used_time": { + "description": "The time the segment was last applied to a translation, in the ISO 8601-1:2019 format. Absent\nif the segment has never been used.", + "type": "string", + "format": "date-time", + "example": "2026-08-05T11:02:18.771Z" + }, + "targets": { + "description": "The translations of this source segment, one per target language.", + "type": "array", + "items": { + "$ref": "#/components/schemas/TranslationMemoryTargetSegment" + } + } + } + }, + "TranslationMemoryTargetSegment": { + "description": "A translation of a source segment in one target language.", + "type": "object", + "required": [ + "target_segment_id", + "target_language", + "target_text" + ], + "properties": { + "target_segment_id": { + "description": "A unique ID assigned to the target segment.", + "type": "string", + "example": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d" + }, + "target_language": { + "$ref": "#/components/schemas/TranslationMemoryTargetLanguage" + }, + "target_text": { + "description": "The translated text.", + "type": "string", + "example": "Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland." + }, + "created_time": { + "description": "The time the translation was added, in the ISO 8601-1:2019 format.", + "type": "string", + "format": "date-time", + "example": "2026-04-01T16:34:25.223Z" + }, + "updated_time": { + "description": "The time the translation was last changed, in the ISO 8601-1:2019 format.", + "type": "string", + "format": "date-time", + "example": "2026-04-01T16:34:25.223Z" + }, + "last_used_time": { + "description": "The time the translation was last applied to a translation request, in the ISO 8601-1:2019\nformat. Absent if it has never been used.", + "type": "string", + "format": "date-time", + "example": "2026-08-05T11:02:18.771Z" + } + } + }, + "CreateTranslationMemoryExport": { + "description": "A reference to a translation memory export job.", + "type": "object", + "required": [ + "job_id", + "parameters" + ], + "properties": { + "job_id": { + "description": "The identifier of the export job. Use it to poll the job's status.", + "type": "string", + "example": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13" + }, + "parameters": { + "type": "object", + "properties": { + "translation_memory_id": { + "$ref": "#/components/schemas/TranslationMemoryId" + } + } + } + } + }, + "TranslationMemoryJob": { + "description": "The state of a translation memory import or export job. Fields that do not apply to the job's\n`operation` are omitted.", + "type": "object", + "required": [ + "job_id", + "product", + "operation", + "created_at", + "updated_at", + "parameters", + "results" + ], + "properties": { + "job_id": { + "description": "The identifier of the job.", + "type": "string", + "example": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" + }, + "product": { + "description": "The DeepL product the job belongs to. Always `translation_memory`.", + "type": "string", + "example": "translation_memory" + }, + "operation": { + "description": "Which kind of job this is.", + "type": "string", + "enum": [ + "import", + "export", + "unspecified" + ], + "example": "import" + }, + "created_at": { + "description": "The time the job was created, in the ISO 8601-1:2019 format.", + "type": "string", + "format": "date-time", + "example": "2026-08-06T15:04:25.223Z" + }, + "updated_at": { + "description": "The time the job last changed state, in the ISO 8601-1:2019 format.", + "type": "string", + "format": "date-time", + "example": "2026-08-06T15:06:11.418Z" + }, + "source_file": { + "description": "The file declared when the import job was created. Omitted for export jobs.", + "type": "object", + "properties": { + "content_type": { + "description": "The MIME type declared for the uploaded file.", + "type": "string", + "example": "application/xml" + }, + "content_length": { + "description": "The size in bytes declared for the uploaded file.", + "type": "integer", + "format": "int64", + "example": 1024 + } + } + }, + "parameters": { + "description": "The parameters the job was created with. Import jobs report `display_name`; export jobs report\n`translation_memory_id`.", + "type": "object", + "properties": { + "translation_memory_id": { + "$ref": "#/components/schemas/TranslationMemoryId" + }, + "display_name": { + "description": "The name given to the translation memory the import creates.", + "type": "string", + "example": "Legal" + } + } + }, + "results": { + "description": "The job's outcome. This array always holds exactly one entry. It is an array so that jobs\nproducing multiple outputs can be represented without a breaking change.", + "type": "array", + "items": { + "$ref": "#/components/schemas/TranslationMemoryJobResult" + } + } + } + }, + "TranslationMemoryJobResult": { + "description": "The status of a translation memory job and, once it finishes, its output.", + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "description": "The job's current state:\n * `awaiting_input` - the job exists but the file has not been uploaded yet (import only)\n * `processing` - the file was received and is being processed\n * `completed` - the job finished successfully\n * `downloaded` - the exported file has been downloaded at least once\n * `failed` - the job did not finish; see `error`\n * `expired` - the job is too old to act on; create a new one", + "type": "string", + "enum": [ + "awaiting_input", + "processing", + "completed", + "downloaded", + "failed", + "expired", + "unspecified" + ], + "example": "completed" + }, + "status_metadata": { + "description": "Extra context for states that need you to act. Present only when there is something to do.", + "type": "object", + "properties": { + "required_action": { + "description": "What the job is waiting for.", + "type": "string", + "example": "Waiting for upload" + } + } + }, + "download_url": { + "description": "A short-lived URL to download the exported TMX file from. Present on completed export jobs\nonly. Download the file rather than storing this URL.", + "type": "string", + "format": "uri", + "example": "https://assets.deepl.com/download/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13" + }, + "expires_at": { + "description": "The time `download_url` stops working, in the ISO 8601-1:2019 format. Present on completed\nexport jobs only.", + "type": "string", + "format": "date-time", + "example": "2026-08-06T16:05:02.771Z" + }, + "error": { + "description": "Why the job failed. Present only when `status` is `failed`.", + "type": "object", + "properties": { + "message": { + "description": "A description of the failure.", + "type": "string", + "example": "The uploaded file is not valid TMX." + } + } + }, + "translation_memory_id": { + "$ref": "#/components/schemas/TranslationMemoryId" + }, + "skipped_segment_count": { + "description": "How many segments in the uploaded file were not imported, for example because they were\nmalformed or duplicated an existing segment. Present on completed import jobs only. A non-zero\nvalue does not mean the import failed.", + "type": "integer", + "format": "int64", + "example": 12 + } + } + }, "TranslationMemoryThreshold": { "type": "integer", "description": "The minimum matching percentage required for a translation memory segment to be applied (recommended to be 75% or higher).", diff --git a/api-reference/openapi.yaml b/api-reference/openapi.yaml index c31301c8..ed1d99c1 100644 --- a/api-reference/openapi.yaml +++ b/api-reference/openapi.yaml @@ -92,11 +92,19 @@ tags: description: Information about API usage and value ranges - name: TranslationMemories description: |- - The translation memory endpoints allow you to interact with your account's translation memories, used to store - and reuse previously created translations. Translation memories can be used in text translation requests by + The translation memory endpoints allow you to manage your account's translation memories, used to store + and reuse previously created translations. You can list and retrieve translation memories, page through + their stored segments, create one by importing a TMX file, export one back to TMX, and delete one. + Editing the contents of an existing translation memory is not supported; import a new one instead. + + Importing and exporting run as background jobs. Create the job, then poll + `GET /v3/translation_memories/jobs/{job_id}` until it reports `completed`. + + Translation memories can be used in text translation requests by specifying the `translation_memory_id` parameter to denote a specific translation memory and the `translation_memory_threshold` which defines the minimum matching percentage required for a translation memory - segment to be applied (recommended to be 75% or higher). + segment to be applied (recommended to be 75% or higher). A translation request fails with `404` if the + translation memory does not exist or does not cover the requested language pair. - name: VoiceAPI description: |- The Voice API provides real-time voice transcription and translation services. @@ -2986,8 +2994,8 @@ paths: type: integer default: 10 minimum: 1 - maximum: 25 - description: The maximum number of translation memories to return. + maximum: 100 + description: The maximum number of translation memories to return. Values above 100 are reduced to 100. responses: 200: description: Returns a list of translation memories. @@ -3041,6 +3049,605 @@ paths: $ref: '#/components/responses/ServiceUnavailable' security: - auth_header: [] + /v3/translation_memories/import: + post: + tags: + - TranslationMemories + summary: Import a translation memory + operationId: createTranslationMemoryImport + description: |- + Create a translation memory by importing a TMX file. + + This endpoint does not accept the file itself. It returns a short-lived `upload_url`, and you + upload the TMX file to that URL with a separate `PUT` request. Processing starts automatically + once the upload completes, so there is no third call to confirm it. + + 1. `POST /v3/translation_memories/import` with the file's name and size. The response returns a + `job_id`, an `upload_url`, and the `expires_at` time after which the URL stops working. + 2. `PUT` the TMX file to `upload_url`. Do not send your `Authorization` header on this request, + because the URL is already signed. + 3. Poll [Retrieve an import or export job](/api-reference/translation-memory/retrieve-a-translation-memory-job) + until the status is `completed`. The new `translation_memory_id` is on the job result. + + Requires an API key with the `translation_memories:write` scope. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - source_file + properties: + source_file: + type: object + required: + - file_name + - content_length + properties: + file_name: + description: |- + The name of the TMX file you intend to upload, including its extension. At most 100 + characters. + type: string + maxLength: 100 + example: legal.tmx + content_type: + description: |- + The MIME type of the file. Defaults to `application/xml`, which is correct for TMX. + TMX is currently the only supported import format. At most 127 characters. + type: string + default: application/xml + maxLength: 127 + example: application/xml + content_length: + description: |- + The size of the file in bytes. Must be greater than 0 and within DeepL's size limit for + TMX imports. An oversize value is rejected with `400 Bad Request`. + type: integer + format: int64 + minimum: 1 + example: 1024 + parameters: + type: object + properties: + display_name: + description: |- + The name to give the new translation memory. If you omit this, the translation memory + is created without a name. + type: string + example: Legal + examples: + Basic: + value: + source_file: + file_name: legal.tmx + content_type: application/xml + content_length: 1024 + parameters: + display_name: Legal + responses: + 202: + description: |- + The import job was created. Upload the file to `upload_url` before `expires_at`. + headers: + X-Trace-ID: + $ref: '#/components/headers/X-Trace-ID' + content: + application/json: + schema: + type: object + required: + - job_id + - upload_url + - expires_at + properties: + job_id: + description: The identifier of the import job. Use it to poll the job's status. + type: string + example: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + upload_url: + description: |- + A signed URL to `PUT` the TMX file to. Do not send your `Authorization` header with the + upload request. + type: string + format: uri + example: https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + expires_at: + description: |- + The time the `upload_url` stops accepting uploads, in the ISO 8601-1:2019 format. After + this time, create a new import job. + type: string + format: date-time + example: '2026-08-06T15:34:25.223Z' + examples: + Basic: + value: + job_id: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + upload_url: https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + expires_at: '2026-08-06T15:34:25.223Z' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/ForbiddenScoped' + 429: + $ref: '#/components/responses/TooManyRequests' + 456: + description: |- + You have reached the maximum number of translation memories for your account. Delete a + translation memory before creating another one. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + $ref: '#/components/responses/InternalServerError' + 503: + $ref: '#/components/responses/ServiceUnavailable' + security: + - auth_header: [] + /v3/translation_memories/jobs/{job_id}: + get: + tags: + - TranslationMemories + summary: Retrieve an import or export job + operationId: getTranslationMemoryJob + description: |- + Retrieve the status of a translation memory import or export job. + + Poll this endpoint after creating a job. The `operation` field tells you which kind of job it is, + and the single entry in `results` carries the status and, once the job finishes, its output: the + new `translation_memory_id` for an import, or a `download_url` for an export. + + Requires an API key with the `translation_memories:read` scope. + parameters: + - name: job_id + in: path + required: true + schema: + type: string + description: The identifier of the job, returned when you created the import or export. + example: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + responses: + 200: + description: Returns the current state of the job. + headers: + X-Trace-ID: + $ref: '#/components/headers/X-Trace-ID' + content: + application/json: + schema: + $ref: '#/components/schemas/TranslationMemoryJob' + examples: + Import awaiting upload: + value: + job_id: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + product: translation_memory + operation: import + created_at: '2026-08-06T15:04:25.223Z' + updated_at: '2026-08-06T15:04:25.223Z' + source_file: + content_type: application/xml + content_length: 1024 + parameters: + display_name: Legal + results: + - status: awaiting_input + status_metadata: + required_action: Waiting for upload + Import completed: + value: + job_id: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + product: translation_memory + operation: import + created_at: '2026-08-06T15:04:25.223Z' + updated_at: '2026-08-06T15:06:11.418Z' + source_file: + content_type: application/xml + content_length: 1024 + parameters: + display_name: Legal + results: + - status: completed + translation_memory_id: a74d88fb-ed2a-4943-a664-a4512398b994 + skipped_segment_count: 12 + Export completed: + value: + job_id: 7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 + product: translation_memory + operation: export + created_at: '2026-08-06T15:04:25.223Z' + updated_at: '2026-08-06T15:05:02.771Z' + parameters: + translation_memory_id: a74d88fb-ed2a-4943-a664-a4512398b994 + results: + - status: completed + download_url: https://assets.deepl.com/download/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 + expires_at: '2026-08-06T16:05:02.771Z' + Failed: + value: + job_id: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + product: translation_memory + operation: import + created_at: '2026-08-06T15:04:25.223Z' + updated_at: '2026-08-06T15:04:58.102Z' + source_file: + content_type: application/xml + content_length: 1024 + parameters: + display_name: Legal + results: + - status: failed + error: + message: The uploaded file is not valid TMX. + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/ForbiddenScoped' + 404: + description: The job could not be found. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 429: + $ref: '#/components/responses/TooManyRequests' + 500: + $ref: '#/components/responses/InternalServerError' + 503: + $ref: '#/components/responses/ServiceUnavailable' + security: + - auth_header: [] + /v3/translation_memories/{translation_memory_id}: + get: + tags: + - TranslationMemories + summary: Retrieve a translation memory + operationId: getTranslationMemory + description: |- + Retrieve a single translation memory by its ID, including its languages and segment count. + + To read the stored segments themselves, use + [List translation memory segments](/api-reference/translation-memory/list-translation-memory-segments). + + Requires an API key with the `translation_memories:read` scope. + parameters: + - name: translation_memory_id + in: path + required: true + schema: + $ref: '#/components/schemas/TranslationMemoryId' + description: The ID of the translation memory to retrieve. + responses: + 200: + description: Returns the translation memory. + headers: + X-Trace-ID: + $ref: '#/components/headers/X-Trace-ID' + content: + application/json: + schema: + $ref: '#/components/schemas/TranslationMemory' + examples: + Basic: + value: + translation_memory_id: a74d88fb-ed2a-4943-a664-a4512398b994 + name: Legal + source_language: en + target_languages: + - es + - de + segment_count: 3542 + creation_time: '2026-04-01T16:34:25.223Z' + updated_time: '2026-08-06T09:12:44.108Z' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/ForbiddenScoped' + 404: + description: |- + The translation memory could not be found. This is also returned for a translation memory that + belongs to another account. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 429: + $ref: '#/components/responses/TooManyRequests' + 500: + $ref: '#/components/responses/InternalServerError' + 503: + $ref: '#/components/responses/ServiceUnavailable' + security: + - auth_header: [] + delete: + tags: + - TranslationMemories + summary: Delete a translation memory + operationId: deleteTranslationMemory + description: |- + Delete a translation memory and all of its segments. + + Deletion is permanent and cannot be undone. Export the translation memory first if you need a + copy of its contents. Translation requests that pass the deleted `translation_memory_id` fail + after this call, so update your integration before deleting. + + Requires an API key with the `translation_memories:write` scope. + parameters: + - name: translation_memory_id + in: path + required: true + schema: + $ref: '#/components/schemas/TranslationMemoryId' + description: The ID of the translation memory to delete. + responses: + 204: + description: The translation memory was deleted. The response has no body. + headers: + X-Trace-ID: + $ref: '#/components/headers/X-Trace-ID' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/ForbiddenScoped' + 404: + description: |- + The translation memory could not be found. This is also returned for a translation memory that + belongs to another account. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 429: + $ref: '#/components/responses/TooManyRequests' + 500: + $ref: '#/components/responses/InternalServerError' + 503: + $ref: '#/components/responses/ServiceUnavailable' + security: + - auth_header: [] + /v3/translation_memories/{translation_memory_id}/segments: + get: + tags: + - TranslationMemories + summary: List translation memory segments + operationId: getTranslationMemorySegments + description: |- + List the segments stored in a translation memory, one page at a time. Each entry is a source + segment with its translations in every target language. + + This endpoint uses cursor-based pagination rather than the numbered pages used by + [List translation memories](/api-reference/translation-memory/list-translation-memories). Omit + `page_cursor` on your first call, then pass the `next_page_cursor` from each response to get the + following page. When a response has no `next_page_cursor`, you have reached the last page. + + Requires an API key with the `translation_memories:read` scope. + parameters: + - name: translation_memory_id + in: path + required: true + schema: + $ref: '#/components/schemas/TranslationMemoryId' + description: The ID of the translation memory whose segments you want to list. + - name: page_size + in: query + schema: + type: integer + default: 50 + minimum: 1 + maximum: 100 + description: |- + The maximum number of segments to return per page. Values above 100 are reduced to 100. + - name: page_cursor + in: query + schema: + type: string + description: |- + The `next_page_cursor` from a previous response. Omit this on your first call. Treat the value + as opaque: do not build or modify it yourself. + - name: filter_text + in: query + schema: + type: string + minLength: 2 + description: |- + Return only segments where this text appears in the source or in any target translation. Must be + at least 2 characters. + - name: filter_case_sensitive + in: query + schema: + type: boolean + default: false + description: Whether `filter_text` is matched case-sensitively. + responses: + 200: + description: Returns a page of segments. + headers: + X-Trace-ID: + $ref: '#/components/headers/X-Trace-ID' + content: + application/json: + schema: + type: object + required: + - segments + - segment_count + properties: + segments: + description: The segments on this page. + type: array + items: + $ref: '#/components/schemas/TranslationMemorySegment' + segment_count: + description: |- + The total number of segments in the translation memory. This is not reduced by + `filter_text`, so a filtered request still reports the full count. Do not use it to + predict how many results a filtered query returns. + type: integer + example: 3542 + next_page_cursor: + description: |- + The cursor to pass as `page_cursor` to retrieve the next page. Absent on the last page. + type: string + example: eyJvZmZzZXQiOjUwfQ + examples: + Basic: + value: + segments: + - source_segment_id: 4f1c2d3e-8a9b-4c5d-9e6f-7a8b9c0d1e2f + source_text: This agreement is governed by the laws of Germany. + created_time: '2026-04-01T16:34:25.223Z' + updated_time: '2026-04-01T16:34:25.223Z' + last_used_time: '2026-08-05T11:02:18.771Z' + targets: + - target_segment_id: 9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d + target_language: de + target_text: Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland. + created_time: '2026-04-01T16:34:25.223Z' + updated_time: '2026-04-01T16:34:25.223Z' + last_used_time: '2026-08-05T11:02:18.771Z' + - target_segment_id: 2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f + target_language: es + target_text: Este contrato se rige por las leyes de Alemania. + created_time: '2026-04-01T16:34:25.223Z' + updated_time: '2026-04-01T16:34:25.223Z' + last_used_time: '2026-07-22T08:41:05.330Z' + segment_count: 3542 + next_page_cursor: eyJvZmZzZXQiOjUwfQ + Last page: + value: + segments: + - source_segment_id: 7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d + source_text: Termination requires 30 days written notice. + created_time: '2026-04-01T16:34:25.223Z' + updated_time: '2026-04-01T16:34:25.223Z' + last_used_time: '2026-08-01T14:20:09.512Z' + targets: + - target_segment_id: 0d1e2f3a-4b5c-4d6e-8f7a-8b9c0d1e2f3a + target_language: de + target_text: Die Kündigung erfordert eine schriftliche Frist von 30 Tagen. + created_time: '2026-04-01T16:34:25.223Z' + updated_time: '2026-04-01T16:34:25.223Z' + last_used_time: '2026-08-01T14:20:09.512Z' + segment_count: 3542 + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/ForbiddenScoped' + 404: + description: |- + The translation memory could not be found. This is also returned for a translation memory that + belongs to another account. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 429: + $ref: '#/components/responses/TooManyRequests' + 500: + $ref: '#/components/responses/InternalServerError' + 503: + $ref: '#/components/responses/ServiceUnavailable' + security: + - auth_header: [] + /v3/translation_memories/{translation_memory_id}/export: + post: + tags: + - TranslationMemories + summary: Export a translation memory + operationId: createTranslationMemoryExport + description: |- + Export a translation memory as a TMX file. + + The export runs as a background job. Poll + [Retrieve an import or export job](/api-reference/translation-memory/retrieve-a-translation-memory-job) + until the status is `completed`, then download the file from the `download_url` on the job result. + That URL is short-lived, so download the file rather than storing the link. + + If a recent export of the same translation memory is still available, DeepL reuses it and returns + `200 OK` with that job instead of starting a new one. Handle both `200` and `202` as success. If a + different export of the same translation memory is still running, the request returns + `409 Conflict`. + + Requires an API key with the `translation_memories:read` scope. + parameters: + - name: translation_memory_id + in: path + required: true + schema: + $ref: '#/components/schemas/TranslationMemoryId' + description: The ID of the translation memory to export. + responses: + 200: + description: An existing export was reused. Poll the returned `job_id` for the download URL. + headers: + X-Trace-ID: + $ref: '#/components/headers/X-Trace-ID' + content: + application/json: + schema: + $ref: '#/components/schemas/CreateTranslationMemoryExport' + examples: + Basic: + value: + job_id: 7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 + parameters: + translation_memory_id: a74d88fb-ed2a-4943-a664-a4512398b994 + 202: + description: The export job was created. Poll the returned `job_id` for the download URL. + headers: + X-Trace-ID: + $ref: '#/components/headers/X-Trace-ID' + content: + application/json: + schema: + $ref: '#/components/schemas/CreateTranslationMemoryExport' + examples: + Basic: + value: + job_id: 7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 + parameters: + translation_memory_id: a74d88fb-ed2a-4943-a664-a4512398b994 + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/ForbiddenScoped' + 404: + description: |- + The translation memory could not be found. This is also returned for a translation memory that + belongs to another account. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 409: + description: |- + An export of this translation memory is already in progress. Poll that job instead of starting + another export. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 429: + $ref: '#/components/responses/TooManyRequests' + 500: + $ref: '#/components/responses/InternalServerError' + 503: + $ref: '#/components/responses/ServiceUnavailable' + security: + - auth_header: [] /v3/style_rules: get: summary: Retrieve style rule lists @@ -6156,6 +6763,233 @@ components: - zh - zh-hans example: es + TranslationMemorySegment: + description: A source segment stored in a translation memory, with its translations. + type: object + required: + - source_segment_id + - source_text + - targets + properties: + source_segment_id: + description: A unique ID assigned to the source segment. + type: string + example: 4f1c2d3e-8a9b-4c5d-9e6f-7a8b9c0d1e2f + source_text: + description: The source text of the segment. + type: string + example: This agreement is governed by the laws of Germany. + created_time: + description: The time the segment was added, in the ISO 8601-1:2019 format. + type: string + format: date-time + example: '2026-04-01T16:34:25.223Z' + updated_time: + description: The time the segment was last changed, in the ISO 8601-1:2019 format. + type: string + format: date-time + example: '2026-04-01T16:34:25.223Z' + last_used_time: + description: |- + The time the segment was last applied to a translation, in the ISO 8601-1:2019 format. Absent + if the segment has never been used. + type: string + format: date-time + example: '2026-08-05T11:02:18.771Z' + targets: + description: The translations of this source segment, one per target language. + type: array + items: + $ref: '#/components/schemas/TranslationMemoryTargetSegment' + TranslationMemoryTargetSegment: + description: A translation of a source segment in one target language. + type: object + required: + - target_segment_id + - target_language + - target_text + properties: + target_segment_id: + description: A unique ID assigned to the target segment. + type: string + example: 9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d + target_language: + $ref: '#/components/schemas/TranslationMemoryTargetLanguage' + target_text: + description: The translated text. + type: string + example: Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland. + created_time: + description: The time the translation was added, in the ISO 8601-1:2019 format. + type: string + format: date-time + example: '2026-04-01T16:34:25.223Z' + updated_time: + description: The time the translation was last changed, in the ISO 8601-1:2019 format. + type: string + format: date-time + example: '2026-04-01T16:34:25.223Z' + last_used_time: + description: |- + The time the translation was last applied to a translation request, in the ISO 8601-1:2019 + format. Absent if it has never been used. + type: string + format: date-time + example: '2026-08-05T11:02:18.771Z' + CreateTranslationMemoryExport: + description: A reference to a translation memory export job. + type: object + required: + - job_id + - parameters + properties: + job_id: + description: The identifier of the export job. Use it to poll the job's status. + type: string + example: 7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 + parameters: + type: object + properties: + translation_memory_id: + $ref: '#/components/schemas/TranslationMemoryId' + TranslationMemoryJob: + description: |- + The state of a translation memory import or export job. Fields that do not apply to the job's + `operation` are omitted. + type: object + required: + - job_id + - product + - operation + - created_at + - updated_at + - parameters + - results + properties: + job_id: + description: The identifier of the job. + type: string + example: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 + product: + description: The DeepL product the job belongs to. Always `translation_memory`. + type: string + example: translation_memory + operation: + description: Which kind of job this is. + type: string + enum: + - import + - export + - unspecified + example: import + created_at: + description: The time the job was created, in the ISO 8601-1:2019 format. + type: string + format: date-time + example: '2026-08-06T15:04:25.223Z' + updated_at: + description: The time the job last changed state, in the ISO 8601-1:2019 format. + type: string + format: date-time + example: '2026-08-06T15:06:11.418Z' + source_file: + description: The file declared when the import job was created. Omitted for export jobs. + type: object + properties: + content_type: + description: The MIME type declared for the uploaded file. + type: string + example: application/xml + content_length: + description: The size in bytes declared for the uploaded file. + type: integer + format: int64 + example: 1024 + parameters: + description: |- + The parameters the job was created with. Import jobs report `display_name`; export jobs report + `translation_memory_id`. + type: object + properties: + translation_memory_id: + $ref: '#/components/schemas/TranslationMemoryId' + display_name: + description: The name given to the translation memory the import creates. + type: string + example: Legal + results: + description: |- + The job's outcome. This array always holds exactly one entry. It is an array so that jobs + producing multiple outputs can be represented without a breaking change. + type: array + items: + $ref: '#/components/schemas/TranslationMemoryJobResult' + TranslationMemoryJobResult: + description: The status of a translation memory job and, once it finishes, its output. + type: object + required: + - status + properties: + status: + description: |- + The job's current state: + * `awaiting_input` - the job exists but the file has not been uploaded yet (import only) + * `processing` - the file was received and is being processed + * `completed` - the job finished successfully + * `downloaded` - the exported file has been downloaded at least once + * `failed` - the job did not finish; see `error` + * `expired` - the job is too old to act on; create a new one + type: string + enum: + - awaiting_input + - processing + - completed + - downloaded + - failed + - expired + - unspecified + example: completed + status_metadata: + description: |- + Extra context for states that need you to act. Present only when there is something to do. + type: object + properties: + required_action: + description: What the job is waiting for. + type: string + example: Waiting for upload + download_url: + description: |- + A short-lived URL to download the exported TMX file from. Present on completed export jobs + only. Download the file rather than storing this URL. + type: string + format: uri + example: https://assets.deepl.com/download/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 + expires_at: + description: |- + The time `download_url` stops working, in the ISO 8601-1:2019 format. Present on completed + export jobs only. + type: string + format: date-time + example: '2026-08-06T16:05:02.771Z' + error: + description: Why the job failed. Present only when `status` is `failed`. + type: object + properties: + message: + description: A description of the failure. + type: string + example: The uploaded file is not valid TMX. + translation_memory_id: + $ref: '#/components/schemas/TranslationMemoryId' + skipped_segment_count: + description: |- + How many segments in the uploaded file were not imported, for example because they were + malformed or duplicated an existing segment. Present on completed import jobs only. A non-zero + value does not mean the import failed. + type: integer + format: int64 + example: 12 TranslationMemoryThreshold: type: integer description: The minimum matching percentage required for a translation memory segment to be applied (recommended to be 75% or higher). diff --git a/api-reference/translation-memory/delete-a-translation-memory.mdx b/api-reference/translation-memory/delete-a-translation-memory.mdx new file mode 100644 index 00000000..b46736b2 --- /dev/null +++ b/api-reference/translation-memory/delete-a-translation-memory.mdx @@ -0,0 +1,13 @@ +--- +openapi: delete /v3/translation_memories/{translation_memory_id} +title: "Delete a translation memory" +description: "Learn how to permanently delete a translation memory and all of its segments." +--- + +Deletion is permanent and removes every segment in the translation memory. There is no recovery step and no trash to restore from. [Export the translation memory](/api-reference/translation-memory/export-a-translation-memory) first if you need a copy. + + + Translation requests that pass a deleted `translation_memory_id` fail. Update your integration to stop referencing the ID before you delete it, not after. + + +A successful delete returns `204 No Content` with an empty body, so check the status code rather than trying to parse a response. diff --git a/api-reference/translation-memory/export-a-translation-memory.mdx b/api-reference/translation-memory/export-a-translation-memory.mdx new file mode 100644 index 00000000..8eb0397d --- /dev/null +++ b/api-reference/translation-memory/export-a-translation-memory.mdx @@ -0,0 +1,74 @@ +--- +openapi: post /v3/translation_memories/{translation_memory_id}/export +title: "Export a translation memory" +description: "Learn how to export a translation memory as a TMX file, including how DeepL reuses recent exports and when it returns 409." +--- + +Exporting runs as a background job. This request starts the job and returns a `job_id`; the TMX file is downloaded separately once the job completes. TMX is currently the only export format, so there is no format parameter. + +## Exporting a translation memory + + + + ```bash + curl -X POST "https://api.deepl.com/v3/translation_memories/a74d88fb-ed2a-4943-a664-a4512398b994/export" \ + -H "Authorization: DeepL-Auth-Key " + ``` + + ```json + { + "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", + "parameters": { + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" + } + } + ``` + + + + Check [the job](/api-reference/translation-memory/retrieve-a-translation-memory-job) until `status` is `completed`, then download the file from `download_url` on the job result. + + ```bash + curl -X GET "https://api.deepl.com/v3/translation_memories/jobs/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13" \ + -H "Authorization: DeepL-Auth-Key " + ``` + + ```json + { + "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", + "product": "translation_memory", + "operation": "export", + "created_at": "2026-08-06T15:04:25.223Z", + "updated_at": "2026-08-06T15:05:02.771Z", + "parameters": { + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" + }, + "results": [ + { + "status": "completed", + "download_url": "https://assets.deepl.com/download/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", + "expires_at": "2026-08-06T16:05:02.771Z" + } + ] + } + ``` + + + +## Handling 200, 202, and 409 + +The status code tells you whether a new job was started: + +| **Status** | **Meaning** | **What to do** | +|---|---|---| +| `202 Accepted` | A new export job was started | Poll `job_id` | +| `200 OK` | A recent export was still available and was reused | Poll `job_id` | +| `409 Conflict` | An export of this translation memory is already running | Poll the job you already have; do not retry the export | + +Treat `200` and `202` the same way. Both return a usable `job_id`, and the only difference is whether DeepL did the work again. + + + Do not retry on `409`. Retrying will keep conflicting with the in-progress export. Poll the `job_id` from your earlier request instead, and hold onto that ID so you can recover without a retry loop. + + +`download_url` is short-lived and stops working at `expires_at`. Download the file when the job completes rather than storing the URL for later. Once the file has been fetched, the job status becomes `downloaded`. If the URL has expired, start a new export. diff --git a/api-reference/translation-memory/import-a-translation-memory.mdx b/api-reference/translation-memory/import-a-translation-memory.mdx new file mode 100644 index 00000000..60c1586f --- /dev/null +++ b/api-reference/translation-memory/import-a-translation-memory.mdx @@ -0,0 +1,102 @@ +--- +openapi: post /v3/translation_memories/import +title: "Import a translation memory" +description: "Learn how to create a translation memory by declaring a TMX file, uploading it to a signed URL, and polling the import job." +--- + +This is how you create a translation memory: there is no endpoint that creates an empty one. Importing a TMX file creates the translation memory and fills it in a single job. + +The request body describes the file you intend to upload; it does not carry the file. DeepL returns a signed `upload_url`, you upload the file to that URL, and processing starts automatically once the upload finishes. + +## Importing a file + + + + Send the file's name and size in bytes. The response returns the `job_id`, the `upload_url`, and the `expires_at` deadline for the upload. + + ```bash + curl -X POST "https://api.deepl.com/v3/translation_memories/import" \ + -H "Authorization: DeepL-Auth-Key " \ + -H "Content-Type: application/json" \ + -d '{ + "source_file": { + "file_name": "legal.tmx", + "content_type": "application/xml", + "content_length": 1024 + }, + "parameters": { + "display_name": "Legal" + } + }' + ``` + + ```json + { + "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "upload_url": "https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "expires_at": "2026-08-06T15:34:25.223Z" + } + ``` + + + + `PUT` the file to `upload_url` before `expires_at`. The URL is already signed, so this request carries no DeepL credentials. + + ```bash + curl -X PUT "https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" \ + -H "Content-Type: application/xml" \ + --data-binary @legal.tmx + ``` + + A successful upload returns no DeepL response body. Processing begins on its own, so there is no call to confirm the upload. + + + + Check [the job](/api-reference/translation-memory/retrieve-a-translation-memory-job) until `status` is `completed` or `failed`. + + ```bash + curl -X GET "https://api.deepl.com/v3/translation_memories/jobs/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" \ + -H "Authorization: DeepL-Auth-Key " + ``` + + On completion, the job result carries the `translation_memory_id` of the new translation memory. + + ```json + { + "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", + "product": "translation_memory", + "operation": "import", + "created_at": "2026-08-06T15:04:25.223Z", + "updated_at": "2026-08-06T15:06:11.418Z", + "source_file": { + "content_type": "application/xml", + "content_length": 1024 + }, + "parameters": { + "display_name": "Legal" + }, + "results": [ + { + "status": "completed", + "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994", + "skipped_segment_count": 12 + } + ] + } + ``` + + + +## Common mistakes + +`content_length` must be greater than 0 and within DeepL's size limit for TMX imports. An oversize value is rejected as `400 Bad Request` with a message pointing at `source_file.content_length`. + +`file_name` accepts at most 100 characters and `content_type` at most 127, both rejected as `400 Bad Request` if longer. TMX is the only supported format, so leave `content_type` unset or set it to `application/xml`. + +A `202 Accepted` means the job exists, not that a translation memory does. Until the upload completes and the job reports `completed`, no translation memory has been created and there is no ID to reference. + +If `expires_at` passes before you upload, the signed URL stops working. Create a new import job to get a fresh URL rather than retrying the old one. + +A non-zero `skipped_segment_count` on a completed import is not a failure. Some segments were not stored, for example because they were malformed or duplicated an existing segment, but the rest imported normally. Check the value if you are reconciling segment counts against your source file. + +If your account has reached its translation memory limit, the request returns `456`. Delete a translation memory before importing another. diff --git a/api-reference/translation-memory/list-translation-memory-segments.mdx b/api-reference/translation-memory/list-translation-memory-segments.mdx new file mode 100644 index 00000000..6bfb1ba5 --- /dev/null +++ b/api-reference/translation-memory/list-translation-memory-segments.mdx @@ -0,0 +1,29 @@ +--- +openapi: get /v3/translation_memories/{translation_memory_id}/segments +title: "List translation memory segments" +description: "Learn how to page through the source segments and translations stored in a translation memory using cursor-based pagination." +--- + +## Paginating with cursors + +This endpoint pages with an opaque cursor, unlike [List translation memories](/api-reference/translation-memory/list-translation-memories), which uses numbered `page` values. Omit `page_cursor` on the first request, then pass each response's `next_page_cursor` to get the following page. A response with no `next_page_cursor` is the last page. + +```bash +# First page +curl -X GET "https://api.deepl.com/v3/translation_memories/a74d88fb-ed2a-4943-a664-a4512398b994/segments?page_size=50" \ + -H "Authorization: DeepL-Auth-Key " + +# Next page, using next_page_cursor from the response above +curl -X GET "https://api.deepl.com/v3/translation_memories/a74d88fb-ed2a-4943-a664-a4512398b994/segments?page_size=50&page_cursor=eyJvZmZzZXQiOjUwfQ" \ + -H "Authorization: DeepL-Auth-Key " +``` + +Treat the cursor as opaque. Its format is not part of the API contract, so do not construct, decode, or increment it yourself. Cursors also encode the filter they were issued under: if you change `filter_text` or `filter_case_sensitive`, start again without a cursor. + + + `segment_count` is the total for the whole translation memory and is not reduced by `filter_text`. A filtered request still reports the full count, so do not use it to decide how many pages of filtered results to expect. Stop paginating when `next_page_cursor` is absent. + + +## Filtering + +`filter_text` matches against both source text and every target translation, and must be at least 2 characters. Matching is case-insensitive unless you set `filter_case_sensitive=true`. diff --git a/api-reference/translation-memory/retrieve-a-translation-memory-job.mdx b/api-reference/translation-memory/retrieve-a-translation-memory-job.mdx new file mode 100644 index 00000000..76541816 --- /dev/null +++ b/api-reference/translation-memory/retrieve-a-translation-memory-job.mdx @@ -0,0 +1,33 @@ +--- +openapi: get /v3/translation_memories/jobs/{job_id} +title: "Retrieve an import or export job" +description: "Learn how to poll a translation memory import or export job and read its status values and results." +--- + +One endpoint covers both [import](/api-reference/translation-memory/import-a-translation-memory) and [export](/api-reference/translation-memory/export-a-translation-memory) jobs. Read `operation` to tell them apart, then read `results[0]` for the status and, once the job finishes, its output. + +`results` always holds exactly one entry. It is an array so that jobs producing multiple outputs can be added later without a breaking change, so index into it rather than assuming a single object. + +## Status values + +| **Status** | **Applies to** | **Meaning** | +|---|---|---| +| `awaiting_input` | Import | The job exists but the file has not been uploaded yet. `status_metadata.required_action` says what is missing. | +| `processing` | Import, export | The file was received and is being processed. | +| `completed` | Import, export | The job succeeded. Read `translation_memory_id` (import) or `download_url` (export). | +| `downloaded` | Export | The exported file has been fetched at least once. | +| `failed` | Import, export | The job did not finish. `error.message` says why. | +| `expired` | Import, export | The job is too old to act on. Create a new one. | + +Poll until the status is `completed`, `downloaded`, `failed`, or `expired`. The other states are transient and will change on their own. + +## Which fields to expect + +Fields that do not apply to a job's `operation` are omitted rather than returned as `null`, so check for a field's presence before reading it: + +- **Import jobs** include `source_file` and `parameters.display_name`. On completion, the result adds `translation_memory_id` and may add `skipped_segment_count`. +- **Export jobs** include `parameters.translation_memory_id`. On completion, the result adds `download_url` and `expires_at`. + + + An import that sits at `awaiting_input` means DeepL is still waiting for the file. Upload it to the `upload_url` from the import response; the status will not advance on its own. + diff --git a/api-reference/translation-memory/retrieve-a-translation-memory.mdx b/api-reference/translation-memory/retrieve-a-translation-memory.mdx new file mode 100644 index 00000000..238b00c1 --- /dev/null +++ b/api-reference/translation-memory/retrieve-a-translation-memory.mdx @@ -0,0 +1,9 @@ +--- +openapi: get /v3/translation_memories/{translation_memory_id} +title: "Retrieve a translation memory" +description: "Learn how to fetch a single translation memory's languages, segment count, and timestamps by its ID." +--- + +This returns the translation memory's metadata, not its contents. To read the stored segments, use [List translation memory segments](/api-reference/translation-memory/list-translation-memory-segments). + +A translation memory that belongs to another account returns `404 Not Found` rather than `403 Forbidden`, so a 404 does not confirm that the ID is unused. diff --git a/docs.json b/docs.json index 6ab5aa0b..38559381 100644 --- a/docs.json +++ b/docs.json @@ -297,7 +297,13 @@ { "group": "Translation Memory", "pages": [ - "api-reference/translation-memory/list-translation-memories" + "api-reference/translation-memory/list-translation-memories", + "api-reference/translation-memory/retrieve-a-translation-memory", + "api-reference/translation-memory/list-translation-memory-segments", + "api-reference/translation-memory/import-a-translation-memory", + "api-reference/translation-memory/export-a-translation-memory", + "api-reference/translation-memory/retrieve-a-translation-memory-job", + "api-reference/translation-memory/delete-a-translation-memory" ], "drilldown": false } diff --git a/docs/admin/permission-scopes.mdx b/docs/admin/permission-scopes.mdx index 4fb1c3f7..6ee94b8e 100644 --- a/docs/admin/permission-scopes.mdx +++ b/docs/admin/permission-scopes.mdx @@ -72,10 +72,23 @@ Each scope grants a [scoped API key](/docs/admin/api-key-permissions) access to | `DELETE` | [`/v3/style_rules/{style_id}/custom_instructions/{instruction_id}`](/api-reference/style-rules/delete-custom-instruction) | - + | **Method** | **Endpoint** | | --- | --- | | `GET` | [`/v3/translation_memories`](/api-reference/translation-memory/list-translation-memories) | + | `GET` | [`/v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/retrieve-a-translation-memory) | + | `GET` | [`/v3/translation_memories/{translation_memory_id}/segments`](/api-reference/translation-memory/list-translation-memory-segments) | + | `POST` | [`/v3/translation_memories/{translation_memory_id}/export`](/api-reference/translation-memory/export-a-translation-memory) | + | `GET` | [`/v3/translation_memories/jobs/{job_id}`](/api-reference/translation-memory/retrieve-a-translation-memory-job) | + + Exporting is a read operation, so it needs `translation_memories:read` rather than `translation_memories:write`, even though it uses `POST`. + + + + | **Method** | **Endpoint** | + | --- | --- | + | `POST` | [`/v3/translation_memories/import`](/api-reference/translation-memory/import-a-translation-memory) | + | `DELETE` | [`/v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/delete-a-translation-memory) | diff --git a/docs/resources/roadmap-and-release-notes.mdx b/docs/resources/roadmap-and-release-notes.mdx index 8b38bc82..f1c97dc8 100644 --- a/docs/resources/roadmap-and-release-notes.mdx +++ b/docs/resources/roadmap-and-release-notes.mdx @@ -5,11 +5,21 @@ rss: true --- -- Support for uploading, modifying, and deleting [translation memories](/docs/customize/using-translation-memories) via API +- Editing the contents of an existing [translation memory](/docs/customize/using-translation-memories) via API - Usage reporting by language pair +## August 11 - Translation Memory Management API +- You can now create, inspect, export, and delete [translation memories](/docs/customize/using-translation-memories) through the API. Translation memories store previously translated segments so the same source text produces consistent output across projects. Previously, the API could only list the translation memories on your account, and everything else had to be done in the DeepL UI. +- [`POST /v3/translation_memories/import`](/api-reference/translation-memory/import-a-translation-memory) creates a translation memory from a TMX file. The request declares the file and returns a signed upload URL plus a `job_id`; you upload the file to that URL and poll the job for the new `translation_memory_id`. +- [`POST /v3/translation_memories/{translation_memory_id}/export`](/api-reference/translation-memory/export-a-translation-memory) exports a translation memory as TMX, also as a background job. +- [`GET /v3/translation_memories/jobs/{job_id}`](/api-reference/translation-memory/retrieve-a-translation-memory-job) reports the status of both import and export jobs. +- [`GET /v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/retrieve-a-translation-memory) retrieves a single translation memory, and [`GET /v3/translation_memories/{translation_memory_id}/segments`](/api-reference/translation-memory/list-translation-memory-segments) pages through its stored segments with cursor-based pagination. +- [`DELETE /v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/delete-a-translation-memory) permanently deletes a translation memory and all of its segments. +- Reading requires an API key with the `translation_memories:read` scope; importing and deleting require `translation_memories:write`. See [permission scopes](/docs/admin/permission-scopes). +- Editing the contents of an existing translation memory is not yet supported. To change what a translation memory contains, import a new one. + ## August 5 - Custom Tag-Level CSV Export - Usage broken down by [custom reporting tag](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags) can now be exported as a CSV report from the account UI, in addition to being available through the [custom tag usage analytics endpoint](/api-reference/admin-api/get-custom-tag-usage-analytics) in the Admin API. Custom tags attribute API usage to a team, project, or other category via the `X-DeepL-Reporting-Tag` request header. - The "Download CSV usage report" button on the [API Keys & Limits tab](https://www.deepl.com/your-account/keys) and the [API Usage tab](https://www.deepl.com/your-account/usage) is now a dropdown: select "Custom tag-level report" or "API key-level report". From 8bc21c2f8ecf5eee7fd5c42dc8e6df2f2c94e603 Mon Sep 17 00:00:00 2001 From: Brianna Delgado Date: Tue, 11 Aug 2026 13:47:14 -0400 Subject: [PATCH 2/3] fix: Remove internal states and rename created_at/updated_at --- api-reference/openapi.json | 53 ++++++++++--------- api-reference/openapi.yaml | 46 ++++++++-------- .../export-a-translation-memory.mdx | 6 +-- .../import-a-translation-memory.mdx | 4 +- .../retrieve-a-translation-memory-job.mdx | 3 +- 5 files changed, 56 insertions(+), 56 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index 4df9168c..f28f9360 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -4465,6 +4465,7 @@ "job_id": { "description": "The identifier of the import job. Use it to poll the job's status.", "type": "string", + "format": "uuid", "example": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" }, "upload_url": { @@ -4543,7 +4544,8 @@ "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "description": "The identifier of the job, returned when you created the import or export.", "example": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" @@ -4568,8 +4570,8 @@ "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", "product": "translation_memory", "operation": "import", - "created_at": "2026-08-06T15:04:25.223Z", - "updated_at": "2026-08-06T15:04:25.223Z", + "creation_time": "2026-08-06T15:04:25.223Z", + "updated_time": "2026-08-06T15:04:25.223Z", "source_file": { "content_type": "application/xml", "content_length": 1024 @@ -4592,8 +4594,8 @@ "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", "product": "translation_memory", "operation": "import", - "created_at": "2026-08-06T15:04:25.223Z", - "updated_at": "2026-08-06T15:06:11.418Z", + "creation_time": "2026-08-06T15:04:25.223Z", + "updated_time": "2026-08-06T15:06:11.418Z", "source_file": { "content_type": "application/xml", "content_length": 1024 @@ -4615,8 +4617,8 @@ "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", "product": "translation_memory", "operation": "export", - "created_at": "2026-08-06T15:04:25.223Z", - "updated_at": "2026-08-06T15:05:02.771Z", + "creation_time": "2026-08-06T15:04:25.223Z", + "updated_time": "2026-08-06T15:05:02.771Z", "parameters": { "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" }, @@ -4634,8 +4636,8 @@ "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", "product": "translation_memory", "operation": "import", - "created_at": "2026-08-06T15:04:25.223Z", - "updated_at": "2026-08-06T15:04:58.102Z", + "creation_time": "2026-08-06T15:04:25.223Z", + "updated_time": "2026-08-06T15:04:58.102Z", "source_file": { "content_type": "application/xml", "content_length": 1024 @@ -4941,7 +4943,7 @@ { "source_segment_id": "4f1c2d3e-8a9b-4c5d-9e6f-7a8b9c0d1e2f", "source_text": "This agreement is governed by the laws of Germany.", - "created_time": "2026-04-01T16:34:25.223Z", + "creation_time": "2026-04-01T16:34:25.223Z", "updated_time": "2026-04-01T16:34:25.223Z", "last_used_time": "2026-08-05T11:02:18.771Z", "targets": [ @@ -4949,7 +4951,7 @@ "target_segment_id": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "target_language": "de", "target_text": "Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland.", - "created_time": "2026-04-01T16:34:25.223Z", + "creation_time": "2026-04-01T16:34:25.223Z", "updated_time": "2026-04-01T16:34:25.223Z", "last_used_time": "2026-08-05T11:02:18.771Z" }, @@ -4957,7 +4959,7 @@ "target_segment_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f", "target_language": "es", "target_text": "Este contrato se rige por las leyes de Alemania.", - "created_time": "2026-04-01T16:34:25.223Z", + "creation_time": "2026-04-01T16:34:25.223Z", "updated_time": "2026-04-01T16:34:25.223Z", "last_used_time": "2026-07-22T08:41:05.330Z" } @@ -4974,7 +4976,7 @@ { "source_segment_id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d", "source_text": "Termination requires 30 days written notice.", - "created_time": "2026-04-01T16:34:25.223Z", + "creation_time": "2026-04-01T16:34:25.223Z", "updated_time": "2026-04-01T16:34:25.223Z", "last_used_time": "2026-08-01T14:20:09.512Z", "targets": [ @@ -4982,7 +4984,7 @@ "target_segment_id": "0d1e2f3a-4b5c-4d6e-8f7a-8b9c0d1e2f3a", "target_language": "de", "target_text": "Die Kündigung erfordert eine schriftliche Frist von 30 Tagen.", - "created_time": "2026-04-01T16:34:25.223Z", + "creation_time": "2026-04-01T16:34:25.223Z", "updated_time": "2026-04-01T16:34:25.223Z", "last_used_time": "2026-08-01T14:20:09.512Z" } @@ -9382,7 +9384,7 @@ "type": "string", "example": "This agreement is governed by the laws of Germany." }, - "created_time": { + "creation_time": { "description": "The time the segment was added, in the ISO 8601-1:2019 format.", "type": "string", "format": "date-time", @@ -9431,7 +9433,7 @@ "type": "string", "example": "Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland." }, - "created_time": { + "creation_time": { "description": "The time the translation was added, in the ISO 8601-1:2019 format.", "type": "string", "format": "date-time", @@ -9462,6 +9464,7 @@ "job_id": { "description": "The identifier of the export job. Use it to poll the job's status.", "type": "string", + "format": "uuid", "example": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13" }, "parameters": { @@ -9481,8 +9484,8 @@ "job_id", "product", "operation", - "created_at", - "updated_at", + "creation_time", + "updated_time", "parameters", "results" ], @@ -9490,6 +9493,7 @@ "job_id": { "description": "The identifier of the job.", "type": "string", + "format": "uuid", "example": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" }, "product": { @@ -9502,18 +9506,17 @@ "type": "string", "enum": [ "import", - "export", - "unspecified" + "export" ], "example": "import" }, - "created_at": { + "creation_time": { "description": "The time the job was created, in the ISO 8601-1:2019 format.", "type": "string", "format": "date-time", "example": "2026-08-06T15:04:25.223Z" }, - "updated_at": { + "updated_time": { "description": "The time the job last changed state, in the ISO 8601-1:2019 format.", "type": "string", "format": "date-time", @@ -9567,16 +9570,14 @@ ], "properties": { "status": { - "description": "The job's current state:\n * `awaiting_input` - the job exists but the file has not been uploaded yet (import only)\n * `processing` - the file was received and is being processed\n * `completed` - the job finished successfully\n * `downloaded` - the exported file has been downloaded at least once\n * `failed` - the job did not finish; see `error`\n * `expired` - the job is too old to act on; create a new one", + "description": "The job's current state:\n * `awaiting_input` - the job exists but the file has not been uploaded yet (import only)\n * `processing` - the file was received and is being processed\n * `completed` - the job finished successfully\n * `failed` - the job did not finish; see `error`\n * `expired` - the job is too old to act on; create a new one", "type": "string", "enum": [ "awaiting_input", "processing", "completed", - "downloaded", "failed", - "expired", - "unspecified" + "expired" ], "example": "completed" }, diff --git a/api-reference/openapi.yaml b/api-reference/openapi.yaml index ed1d99c1..f9b05677 100644 --- a/api-reference/openapi.yaml +++ b/api-reference/openapi.yaml @@ -3145,6 +3145,7 @@ paths: job_id: description: The identifier of the import job. Use it to poll the job's status. type: string + format: uuid example: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 upload_url: description: |- @@ -3208,6 +3209,7 @@ paths: required: true schema: type: string + format: uuid description: The identifier of the job, returned when you created the import or export. example: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 responses: @@ -3226,8 +3228,8 @@ paths: job_id: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 product: translation_memory operation: import - created_at: '2026-08-06T15:04:25.223Z' - updated_at: '2026-08-06T15:04:25.223Z' + creation_time: '2026-08-06T15:04:25.223Z' + updated_time: '2026-08-06T15:04:25.223Z' source_file: content_type: application/xml content_length: 1024 @@ -3242,8 +3244,8 @@ paths: job_id: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 product: translation_memory operation: import - created_at: '2026-08-06T15:04:25.223Z' - updated_at: '2026-08-06T15:06:11.418Z' + creation_time: '2026-08-06T15:04:25.223Z' + updated_time: '2026-08-06T15:06:11.418Z' source_file: content_type: application/xml content_length: 1024 @@ -3258,8 +3260,8 @@ paths: job_id: 7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 product: translation_memory operation: export - created_at: '2026-08-06T15:04:25.223Z' - updated_at: '2026-08-06T15:05:02.771Z' + creation_time: '2026-08-06T15:04:25.223Z' + updated_time: '2026-08-06T15:05:02.771Z' parameters: translation_memory_id: a74d88fb-ed2a-4943-a664-a4512398b994 results: @@ -3271,8 +3273,8 @@ paths: job_id: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 product: translation_memory operation: import - created_at: '2026-08-06T15:04:25.223Z' - updated_at: '2026-08-06T15:04:58.102Z' + creation_time: '2026-08-06T15:04:25.223Z' + updated_time: '2026-08-06T15:04:58.102Z' source_file: content_type: application/xml content_length: 1024 @@ -3504,20 +3506,20 @@ paths: segments: - source_segment_id: 4f1c2d3e-8a9b-4c5d-9e6f-7a8b9c0d1e2f source_text: This agreement is governed by the laws of Germany. - created_time: '2026-04-01T16:34:25.223Z' + creation_time: '2026-04-01T16:34:25.223Z' updated_time: '2026-04-01T16:34:25.223Z' last_used_time: '2026-08-05T11:02:18.771Z' targets: - target_segment_id: 9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d target_language: de target_text: Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland. - created_time: '2026-04-01T16:34:25.223Z' + creation_time: '2026-04-01T16:34:25.223Z' updated_time: '2026-04-01T16:34:25.223Z' last_used_time: '2026-08-05T11:02:18.771Z' - target_segment_id: 2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f target_language: es target_text: Este contrato se rige por las leyes de Alemania. - created_time: '2026-04-01T16:34:25.223Z' + creation_time: '2026-04-01T16:34:25.223Z' updated_time: '2026-04-01T16:34:25.223Z' last_used_time: '2026-07-22T08:41:05.330Z' segment_count: 3542 @@ -3527,14 +3529,14 @@ paths: segments: - source_segment_id: 7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d source_text: Termination requires 30 days written notice. - created_time: '2026-04-01T16:34:25.223Z' + creation_time: '2026-04-01T16:34:25.223Z' updated_time: '2026-04-01T16:34:25.223Z' last_used_time: '2026-08-01T14:20:09.512Z' targets: - target_segment_id: 0d1e2f3a-4b5c-4d6e-8f7a-8b9c0d1e2f3a target_language: de target_text: Die Kündigung erfordert eine schriftliche Frist von 30 Tagen. - created_time: '2026-04-01T16:34:25.223Z' + creation_time: '2026-04-01T16:34:25.223Z' updated_time: '2026-04-01T16:34:25.223Z' last_used_time: '2026-08-01T14:20:09.512Z' segment_count: 3542 @@ -6779,7 +6781,7 @@ components: description: The source text of the segment. type: string example: This agreement is governed by the laws of Germany. - created_time: + creation_time: description: The time the segment was added, in the ISO 8601-1:2019 format. type: string format: date-time @@ -6819,7 +6821,7 @@ components: description: The translated text. type: string example: Dieser Vertrag unterliegt dem Recht der Bundesrepublik Deutschland. - created_time: + creation_time: description: The time the translation was added, in the ISO 8601-1:2019 format. type: string format: date-time @@ -6846,6 +6848,7 @@ components: job_id: description: The identifier of the export job. Use it to poll the job's status. type: string + format: uuid example: 7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13 parameters: type: object @@ -6861,14 +6864,15 @@ components: - job_id - product - operation - - created_at - - updated_at + - creation_time + - updated_time - parameters - results properties: job_id: description: The identifier of the job. type: string + format: uuid example: 0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40 product: description: The DeepL product the job belongs to. Always `translation_memory`. @@ -6880,14 +6884,13 @@ components: enum: - import - export - - unspecified example: import - created_at: + creation_time: description: The time the job was created, in the ISO 8601-1:2019 format. type: string format: date-time example: '2026-08-06T15:04:25.223Z' - updated_at: + updated_time: description: The time the job last changed state, in the ISO 8601-1:2019 format. type: string format: date-time @@ -6936,7 +6939,6 @@ components: * `awaiting_input` - the job exists but the file has not been uploaded yet (import only) * `processing` - the file was received and is being processed * `completed` - the job finished successfully - * `downloaded` - the exported file has been downloaded at least once * `failed` - the job did not finish; see `error` * `expired` - the job is too old to act on; create a new one type: string @@ -6944,10 +6946,8 @@ components: - awaiting_input - processing - completed - - downloaded - failed - expired - - unspecified example: completed status_metadata: description: |- diff --git a/api-reference/translation-memory/export-a-translation-memory.mdx b/api-reference/translation-memory/export-a-translation-memory.mdx index 8eb0397d..e3c30ad7 100644 --- a/api-reference/translation-memory/export-a-translation-memory.mdx +++ b/api-reference/translation-memory/export-a-translation-memory.mdx @@ -38,8 +38,8 @@ Exporting runs as a background job. This request starts the job and returns a `j "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", "product": "translation_memory", "operation": "export", - "created_at": "2026-08-06T15:04:25.223Z", - "updated_at": "2026-08-06T15:05:02.771Z", + "creation_time": "2026-08-06T15:04:25.223Z", + "updated_time": "2026-08-06T15:05:02.771Z", "parameters": { "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" }, @@ -71,4 +71,4 @@ Treat `200` and `202` the same way. Both return a usable `job_id`, and the only Do not retry on `409`. Retrying will keep conflicting with the in-progress export. Poll the `job_id` from your earlier request instead, and hold onto that ID so you can recover without a retry loop. -`download_url` is short-lived and stops working at `expires_at`. Download the file when the job completes rather than storing the URL for later. Once the file has been fetched, the job status becomes `downloaded`. If the URL has expired, start a new export. +`download_url` is short-lived and stops working at `expires_at`. Download the file when the job completes rather than storing the URL for later. If the URL has expired, start a new export. diff --git a/api-reference/translation-memory/import-a-translation-memory.mdx b/api-reference/translation-memory/import-a-translation-memory.mdx index 60c1586f..3fd9c1fb 100644 --- a/api-reference/translation-memory/import-a-translation-memory.mdx +++ b/api-reference/translation-memory/import-a-translation-memory.mdx @@ -66,8 +66,8 @@ The request body describes the file you intend to upload; it does not carry the "job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40", "product": "translation_memory", "operation": "import", - "created_at": "2026-08-06T15:04:25.223Z", - "updated_at": "2026-08-06T15:06:11.418Z", + "creation_time": "2026-08-06T15:04:25.223Z", + "updated_time": "2026-08-06T15:06:11.418Z", "source_file": { "content_type": "application/xml", "content_length": 1024 diff --git a/api-reference/translation-memory/retrieve-a-translation-memory-job.mdx b/api-reference/translation-memory/retrieve-a-translation-memory-job.mdx index 76541816..7821a0d4 100644 --- a/api-reference/translation-memory/retrieve-a-translation-memory-job.mdx +++ b/api-reference/translation-memory/retrieve-a-translation-memory-job.mdx @@ -15,11 +15,10 @@ One endpoint covers both [import](/api-reference/translation-memory/import-a-tra | `awaiting_input` | Import | The job exists but the file has not been uploaded yet. `status_metadata.required_action` says what is missing. | | `processing` | Import, export | The file was received and is being processed. | | `completed` | Import, export | The job succeeded. Read `translation_memory_id` (import) or `download_url` (export). | -| `downloaded` | Export | The exported file has been fetched at least once. | | `failed` | Import, export | The job did not finish. `error.message` says why. | | `expired` | Import, export | The job is too old to act on. Create a new one. | -Poll until the status is `completed`, `downloaded`, `failed`, or `expired`. The other states are transient and will change on their own. +Poll until the status is `completed`, `failed`, or `expired`. The other states are transient and will change on their own. ## Which fields to expect From 017946cfecaf124aabb4604253a535c3fc89b78b Mon Sep 17 00:00:00 2001 From: Brianna Delgado Date: Tue, 11 Aug 2026 13:54:15 -0400 Subject: [PATCH 3/3] docs: State the 1 GB limit for TMX import content_length Replaces "within DeepL's size limit for TMX imports" in both specs and on the import page. Co-Authored-By: Claude Opus 5 --- api-reference/openapi.json | 2 +- api-reference/openapi.yaml | 4 ++-- .../translation-memory/import-a-translation-memory.mdx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index f28f9360..6a0bf89c 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -4407,7 +4407,7 @@ "example": "application/xml" }, "content_length": { - "description": "The size of the file in bytes. Must be greater than 0 and within DeepL's size limit for\nTMX imports. An oversize value is rejected with `400 Bad Request`.", + "description": "The size of the file in bytes. Must be greater than 0 and at most 1 GB. An oversize\nvalue is rejected with `400 Bad Request`.", "type": "integer", "format": "int64", "minimum": 1, diff --git a/api-reference/openapi.yaml b/api-reference/openapi.yaml index f9b05677..80514aab 100644 --- a/api-reference/openapi.yaml +++ b/api-reference/openapi.yaml @@ -3102,8 +3102,8 @@ paths: example: application/xml content_length: description: |- - The size of the file in bytes. Must be greater than 0 and within DeepL's size limit for - TMX imports. An oversize value is rejected with `400 Bad Request`. + The size of the file in bytes. Must be greater than 0 and at most 1 GB. An oversize + value is rejected with `400 Bad Request`. type: integer format: int64 minimum: 1 diff --git a/api-reference/translation-memory/import-a-translation-memory.mdx b/api-reference/translation-memory/import-a-translation-memory.mdx index 3fd9c1fb..5ef05654 100644 --- a/api-reference/translation-memory/import-a-translation-memory.mdx +++ b/api-reference/translation-memory/import-a-translation-memory.mdx @@ -89,7 +89,7 @@ The request body describes the file you intend to upload; it does not carry the ## Common mistakes -`content_length` must be greater than 0 and within DeepL's size limit for TMX imports. An oversize value is rejected as `400 Bad Request` with a message pointing at `source_file.content_length`. +`content_length` must be greater than 0 and at most 1 GB. An oversize value is rejected as `400 Bad Request` with a message pointing at `source_file.content_length`. `file_name` accepts at most 100 characters and `content_type` at most 127, both rejected as `400 Bad Request` if longer. TMX is the only supported format, so leave `content_type` unset or set it to `application/xml`.