# Cancel customiser language translation (`cancel_customiser_language_translation`)

Stops the queued or running AI translation that is writing a customiser language draft copy. The translation is marked cancelled and a language in the translating status returns to draft. Draft values already written are kept. A cancelled translation cannot be resumed through this connector: a new translation can only be started in the merchant admin. Cancelling unblocks a publish or a delete refused with ai_translation_in_progress. It is refused with ai_translation_not_cancellable when no translation is queued or running. The call requires confirm true and changes nothing without it; dry_run true checks the cancel without stopping anything. It requires the customisers:write scope and an idempotency_key.

- Source URL: https://www.signcustomiser.com/help/mcp/tools/cancel_customiser_language_translation/
- Markdown URL: https://www.signcustomiser.com/help/mcp/tools/cancel_customiser_language_translation.md
- MCP endpoint: https://mcp.signcustomiser.com
- Required scope: `customisers:write`
- Behaviour: destructive write

## Purpose

Stop a running AI translation, usually so the language can be published or deleted, after the merchant has agreed to it.

## Annotations

| Hint | Value |
| --- | --- |
| readOnlyHint | false |
| destructiveHint | true |
| idempotentHint | true |
| openWorldHint | false |

## Prerequisites

- A customiser id from list_customisers and a language code from list_customiser_languages.
- A queued or running AI translation; the ai_translation member of get_customiser_language shows its status.
- The merchant explicit agreement, sent as confirm true.
- A merchant has connected this store and approved the customisers:write scope.

## Side effects

- Marks the running AI translation cancelled and stops its remaining work. It cannot be resumed.
- Returns a language in the translating status to draft. Draft values already written are kept.
- Clears the customiser cached storefront configuration and runs the language sync.
- Claims the idempotency_key for at least 24 hours.

## Arguments

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `customiser_id` | integer | yes | The customiser id, from list_customisers. |
| `language_code` | string | yes | The BCP 47 language tag the language is addressed by, such as fr or pt-BR, from list_customiser_languages. |
| `confirm` | boolean | unless `dry_run` is true | Must be true to apply the change, because the running AI translation stops and cannot be resumed through this connector. Without it the call returns a confirmation_required error and changes nothing. |
| `idempotency_key` | string | unless `dry_run` is true | A client-generated key unique to this logical write, such as a UUID. Required: repeating the call with the same key and the same arguments replays the original result instead of writing twice, and the same key with different arguments is an idempotency_key_conflict. |
| `dry_run` | boolean | no | When true, the cancel is checked against the state of the latest AI translation is checked and nothing is written: the result is the same verdict the apply would have reached, and a problem the apply would have raised comes back as the same tool error. Defaults to false. A dry run claims no key. A dry run does not require the confirm argument. |

## Result

The language record after the cancel, whose ai_translation status is cancelled. A dry run returns the cancel verdict instead and stops nothing.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `request_id` | string | yes |  |
| `store` | object | yes |  |
| `store.id` | integer | yes |  |
| `store.name` | string | yes |  |
| `customiser_id` | integer | yes |  |
| `language` | object | no | The language after the cancel. Absent on a dry run. |
| `language.language_code` | string | yes |  |
| `language.source_language_code` | string or null | no |  |
| `language.display_name` | string or null | no |  |
| `language.status` | string | yes |  |
| `language.is_default` | boolean | yes |  |
| `language.is_authoring` | boolean | yes |  |
| `language.translation_summary` | object or null | no |  |
| `language.ai_translation` | object or null | no |  |
| `language.published_at` | string or null | no |  |
| `language.updated_at` | string or null | no |  |
| `dry_run` | boolean | no | Present and true only when dry_run was requested. |
| `valid` | boolean | no | Present only on a dry run, and always true: a failing dry run returns a tool error instead. |
| `verdict` | object | no | Present only on a dry run: the cancel verdict. |
| `idempotent_replay` | boolean | no | True when this call replayed a previously recorded response for the same idempotency_key instead of applying the write again. |

## Error cases

| Code | Recovery |
| --- | --- |
| `ai_translation_not_cancellable` | Read reason. no_translation: the language never had one. not_running: the most recent translation already finished, failed or was cancelled. |
| `confirmation_required` | Send confirm true once the merchant has agreed to stop the translation. Nothing was cancelled. |
| `idempotency_subject_changed` | The language changed after the recorded cancel. Issue a new key and re-check the language before writing again. |
| `insufficient_scope` | The connection was approved without customisers:write. |
| `resource_not_found` | No customiser with that id, or no language with that code on it. |
| `resource_state_conflict` | The translation changed state while the cancel ran. Read the language again. |

## Example: Stop a running translation the merchant asked to cancel

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "cancel_customiser_language_translation",
    "arguments": {
      "customiser_id": 42,
      "language_code": "fr",
      "confirm": true,
      "idempotency_key": "f2a3b4c5-d6e7-4f89-90a1-b2c3d4e5f607"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t",
      "store": {
        "id": 12,
        "name": "Demo Signs"
      },
      "customiser_id": 42,
      "language": {
        "language_code": "fr",
        "source_language_code": "en",
        "display_name": "French",
        "status": "draft",
        "is_default": false,
        "is_authoring": false,
        "translation_summary": {
          "key_count": 184,
          "required_key_count": 96,
          "translated_key_count": 40,
          "retired_key_count": 0,
          "missing_required_key_count": 70,
          "publishable": false
        },
        "ai_translation": {
          "object": "language_ai_translation",
          "status": "cancelled",
          "cancellable": false,
          "chunks_total": 4,
          "keys_targeted": 184,
          "error_summary": null,
          "started_at": "2026-05-04T09:20:00Z",
          "finished_at": "2026-05-04T09:30:00Z",
          "created_at": "2026-05-04T09:19:58Z",
          "updated_at": "2026-05-04T09:30:00Z"
        },
        "published_at": null,
        "updated_at": "2026-05-04T09:30:00Z"
      },
      "idempotent_replay": false
    }
  }
}
```

## Example: No translation is running (error)

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "cancel_customiser_language_translation",
    "arguments": {
      "customiser_id": 42,
      "language_code": "fr",
      "dry_run": true
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "structuredContent": {
      "error": {
        "code": "ai_translation_not_cancellable",
        "title": "AI translation not cancellable",
        "detail": "The language's most recent AI translation is completed, so there is nothing to cancel. Only a queued or running translation can be cancelled; a finished one is already in its final state.",
        "recovery": "There is no queued or running AI translation to cancel. Read reason, and read the language ai_translation to see the state of its latest translation.",
        "reason": "not_running"
      }
    }
  }
}
```
