# Cancel a video task `POST /v1/video/tasks/{task_id}/cancel` Requires service.use permission and Idempotency-Key. Send an empty request body. Before generation starts, cancellation takes effect immediately: the task becomes cancelled and the full reserved amount is released. If billing is paused, the response still says effective and the release completes later. Once generation has started, cancellation returns not_effective; the task continues and billing proceeds normally. Retrying the same request with the same key replays the original response. Using that key for another task returns idempotency_conflict. A new key refers to the same cancellation operation and saves the task status at that response. A finished task with no cancellation operation returns task_not_cancellable. Unknown tasks and tasks outside your team return task_not_found, as with a task query. A rejected request does not reserve a new key. - Put your API key after `Authorization: Bearer`; send no request body. - Send an `Idempotency-Key`; the same key replays the original response. - Before generation starts, cancellation takes effect at once: the task becomes `cancelled` and the whole reservation is released. - After generation starts, the answer is `not_effective`; the task continues and is settled as usual. ## Headers | Field | Type | Requirement | Description | | --- | --- | --- | --- | | `Authorization` | string | Required | Bearer, a space and your API key. A missing or invalid key returns 401 authentication_failed. | | `X-Request-ID` | string | Optional | Your own request identifier. An invalid or repeated value is replaced; the value used comes back in the X-Request-ID response header. | | `Idempotency-Key` | string | Required | Generate a new value for every new request and save it; it starts with a letter or digit. When the outcome is unknown, retry with the same key and the original body to get the original response; the same key with a different body returns 409 idempotency_conflict. Keys belong to your team, not to one API key. Length: 8–128. Characters: A–Z a–z 0–9 . _ : -. | ## Path parameters | Field | Type | Requirement | Description | | --- | --- | --- | --- | | `task_id` | string | Required | ID of the video task to retrieve | ## Returns · HTTP 202 A permanent snapshot of the cancellation operation. effective means Cancelled; not_effective means Not cancelled. Continue checking the task and its billing. | Field | Type | Requirement | Description | | --- | --- | --- | --- | | `task_id` | string | Required | Video Task ID belonging to your team. Length: 4–128. Characters: A–Z a–z 0–9 _ -. | | `cancel_operation_id` | string | Required | Stable Cancellation ID. New idempotency keys for the same task refer to this same ID. Length: 4–128. Characters: A–Z a–z 0–9 _ -. | | `cancel_status` | string | Required | Cancellation status. Reports Cancelled or Not cancelled immediately. `requested`: Requested; `effective`: Cancelled; `not_effective`: Not cancelled; `failed`: Failed; | | `task_status_at_response` | string | Required | Task status saved when this idempotency key first receives a response. Replays keep this saved status. `queued`: Queued; `processing`: Processing; `succeeded`: Succeeded; `failed`: Failed; `cancelled`: Cancelled; | | `reason_code` | string | Required, nullable | Null when cancellation takes effect; generation_already_started when generation has already started and the task is not cancelled. Length: 1–96. Characters: a–z 0–9 _. | ### Response headers | Field | Type | Requirement | Description | | --- | --- | --- | --- | | `X-Request-ID` | string | Required | The identifier of this request: the valid value you sent, or its replacement. | | `Location` | string | Required | Canonical relative lookup path for the accepted object. | ## Status codes Each `error.code` and what to do about it is on [Error codes](./api-docs-en-api-errors.md). | Status | Meaning | error.code | | --- | --- | --- | | 202 | A permanent snapshot of the cancellation operation. Response headers: `Location` | | | 400 | The submitted request is not valid. | invalid_request | | 401 | The API key is missing or invalid. Response headers: `WWW-Authenticate` | authentication_failed | | 404 | No task with this ID is visible to your team. | task_not_found | | 409 | The idempotency key belongs to a different request, or the finished task has no cancellation operation to refer to. | idempotency_conflict, idempotency_replay_expired, task_not_cancellable | | 429 | The request rate limit blocks this read operation. Response headers: `Retry-After` | rate_limited | | 500 | Internal error. | internal_error | | 503 | The service is temporarily unavailable. | dependency_unavailable | ## Related guides - [Task status and results](./api-docs-en-video-tasks.md) - [Idempotency and retries](./api-docs-en-idempotency.md) - [How billing works](./api-docs-en-billing.md) ## Request examples ```bash IDEMPOTENCY_KEY=${IDEMPOTENCY_KEY:-$(uuidgen)} printf '%s\n' "Save IDEMPOTENCY_KEY for retries: ${IDEMPOTENCY_KEY}" >&2 curl --request POST "${CHENVOYA_BASE_URL}/v1/video/tasks/${TASK_ID}/cancel" \ --header "Authorization: Bearer ${CHENVOYA_API_KEY}" \ --header "Idempotency-Key: ${IDEMPOTENCY_KEY}" ``` ```python import urllib.request import urllib.parse import json import os import sys import re import uuid key = os.environ["CHENVOYA_API_KEY"].strip() if not key or re.search(r"[\x00-\x1f\x7f-\x9f]", key): sys.exit("Set CHENVOYA_API_KEY to a non-empty value without control characters") base = os.environ["CHENVOYA_BASE_URL"].rstrip("/") headers = {"Authorization": "Bearer " + key} idempotency = os.environ.get("IDEMPOTENCY_KEY") or str(uuid.uuid4()) print("Save IDEMPOTENCY_KEY for retries: " + idempotency, file=sys.stderr) headers["Idempotency-Key"] = idempotency TASK_ID = os.environ["TASK_ID"] path = "/v1/video/tasks/{task_id}/cancel".replace("{task_id}", urllib.parse.quote(TASK_ID, safe="")) request = urllib.request.Request(base + path, data=None, headers=headers, method="POST") try: with urllib.request.urlopen(request) as response: print(response.read().decode("utf-8")) except urllib.request.HTTPError as error: print(error.read().decode("utf-8")) sys.exit(1) ``` ```javascript // Needs Node.js 20 or later; save it as a .mjs file and run it. const key = process.env.CHENVOYA_API_KEY?.trim(); if (!key || /[\x00-\x1f\x7f-\x9f]/.test(key)) { console.error("Set CHENVOYA_API_KEY to a non-empty value without control characters"); process.exit(1); } const base = process.env.CHENVOYA_BASE_URL; if (!base) throw new Error("Set CHENVOYA_BASE_URL"); const headers = {Authorization: `Bearer ${key}`}; const idempotency = process.env.IDEMPOTENCY_KEY || crypto.randomUUID(); console.error("Save IDEMPOTENCY_KEY for retries: " + idempotency); headers["Idempotency-Key"] = idempotency; const TASK_ID = process.env.TASK_ID; if (!TASK_ID) throw new Error("Set TASK_ID"); const path = "/v1/video/tasks/{task_id}/cancel".replace("{task_id}", encodeURIComponent(TASK_ID)); const response = await fetch(base.replace(/\/$/, "") + path, {method: "POST", headers, body: undefined}); console.log(await response.text()); if (!response.ok) process.exitCode = 1; ``` ```go package main import ( "crypto/rand" "fmt" "io" "net/http" "net/url" "os" "regexp" "strings" ) func main() { key := strings.TrimSpace(os.Getenv("CHENVOYA_API_KEY")) if key == "" || regexp.MustCompile(`[\x00-\x1f\x7f-\x9f]`).MatchString(key) { fail("Set CHENVOYA_API_KEY to a non-empty value without control characters") } base := strings.TrimRight(os.Getenv("CHENVOYA_BASE_URL"), "/") if base == "" { fail("Set CHENVOYA_BASE_URL") } taskID := os.Getenv("TASK_ID") if taskID == "" { fail("Set TASK_ID") } path := strings.Replace("/v1/video/tasks/{task_id}/cancel", "{task_id}", url.PathEscape(taskID), 1) request, err := http.NewRequest("POST", base+path, nil) if err != nil { fail(err.Error()) } request.Header.Set("Authorization", "Bearer "+key) idempotency := os.Getenv("IDEMPOTENCY_KEY") if idempotency == "" { idempotency = newOperationKey() } fmt.Fprintln(os.Stderr, "Save IDEMPOTENCY_KEY for retries: "+idempotency) request.Header.Set("Idempotency-Key", idempotency) response, err := http.DefaultClient.Do(request) if err != nil { fail(err.Error()) } text, err := io.ReadAll(response.Body) response.Body.Close() if err != nil { fail(err.Error()) } fmt.Println(string(text)) if response.StatusCode < 200 || response.StatusCode > 299 { os.Exit(1) } } func fail(message string) { fmt.Fprintln(os.Stderr, message) os.Exit(1) } // newOperationKey returns a random UUID; save it to retry with the same value. func newOperationKey() string { var b [16]byte if _, err := rand.Read(b[:]); err != nil { fail(err.Error()) } b[6] = b[6]&0x0f | 0x40 b[8] = b[8]&0x3f | 0x80 return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16]) } ``` ## Response examples 202 ```json { "task_id": "task_32ae2a5177b310d3bc1ec83a98f63889", "cancel_operation_id": "cancelop_ab99971fbec36056096527b7494b2059", "cancel_status": "requested", "task_status_at_response": "processing", "reason_code": null } ``` 400 ```json { "error": { "code": "invalid_request", "message": "request parameters are invalid" }, "trace_id": "trace_error_example" } ``` 401 ```json { "error": { "code": "authentication_failed", "message": "authentication failed" }, "trace_id": "trace_error_example" } ``` 404 ```json { "error": { "code": "task_not_found", "message": "task was not found" }, "trace_id": "trace_error_example" } ``` 409 ```json { "error": { "code": "task_not_cancellable", "message": "the terminal task has no cancellation operation" }, "trace_id": "trace_error_example" } ``` 429 ```json { "error": { "code": "rate_limited", "message": "request rate limit exceeded" }, "trace_id": "trace_error_example" } ``` 500 ```json { "error": { "code": "internal_error", "message": "internal server error" }, "trace_id": "trace_error_example" } ``` 503 ```json { "error": { "code": "dependency_unavailable", "message": "service is not ready" }, "trace_id": "trace_error_example" } ```