curl --request POST \
--url https://api.moda.app/v1/tasks/{task_id}/cancel \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.moda.app/v1/tasks/{task_id}/cancel"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.moda.app/v1/tasks/{task_id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.moda.app/v1/tasks/{task_id}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.moda.app/v1/tasks/{task_id}/cancel"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.moda.app/v1/tasks/{task_id}/cancel")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.moda.app/v1/tasks/{task_id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "task_01HT9WK8N3M2J4A5Z6P7Q8R9TV",
"kind": "design",
"status": "queued",
"attempt": 123,
"max_attempts": 123,
"attempts_started": 123,
"links": {
"self": "<string>",
"events": "<string>",
"cancel": "<string>",
"canvas": "<string>"
},
"created_at": "<string>",
"started_at": "<string>",
"completed_at": "<string>",
"progress": {
"percent": 123,
"step": "<string>",
"message": "<string>"
},
"first_started_at": "<string>",
"input": {},
"result": {
"canvas_id": "<string>",
"canvas_url": "<string>",
"canvas_name": "<string>",
"conversation_id": "<string>",
"source_canvas_id": "<string>",
"source_canvas_url": "<string>",
"theme_canvas_id": "<string>",
"export": {
"url": "<string>",
"format": "<string>",
"status": "<string>",
"page_count": 123
}
},
"error": {},
"credits": {
"credits_used": 123,
"credits_remaining": 123
},
"retry_after_ms": 123
}{
"id": "task_01HT9WK8N3M2J4A5Z6P7Q8R9TV",
"kind": "design",
"status": "queued",
"attempt": 123,
"max_attempts": 123,
"attempts_started": 123,
"links": {
"self": "<string>",
"events": "<string>",
"cancel": "<string>",
"canvas": "<string>"
},
"created_at": "<string>",
"started_at": "<string>",
"completed_at": "<string>",
"progress": {
"percent": 123,
"step": "<string>",
"message": "<string>"
},
"first_started_at": "<string>",
"input": {},
"result": {
"canvas_id": "<string>",
"canvas_url": "<string>",
"canvas_name": "<string>",
"conversation_id": "<string>",
"source_canvas_id": "<string>",
"source_canvas_url": "<string>",
"theme_canvas_id": "<string>",
"export": {
"url": "<string>",
"format": "<string>",
"status": "<string>",
"page_count": 123
}
},
"error": {},
"credits": {
"credits_used": 123,
"credits_remaining": 123
},
"retry_after_ms": 123
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}Cancel Task
Request cancellation of an in-flight design task.
Returns 200 with the canceled Task envelope if the executor acknowledges
within 5 seconds, or 202 with a canceling Task envelope if the signal
was published but the executor hasn’t acknowledged yet. Clients should poll
the Task status (or subscribe to events) to observe the terminal transition.
curl --request POST \
--url https://api.moda.app/v1/tasks/{task_id}/cancel \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.moda.app/v1/tasks/{task_id}/cancel"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.moda.app/v1/tasks/{task_id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.moda.app/v1/tasks/{task_id}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.moda.app/v1/tasks/{task_id}/cancel"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.moda.app/v1/tasks/{task_id}/cancel")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.moda.app/v1/tasks/{task_id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "task_01HT9WK8N3M2J4A5Z6P7Q8R9TV",
"kind": "design",
"status": "queued",
"attempt": 123,
"max_attempts": 123,
"attempts_started": 123,
"links": {
"self": "<string>",
"events": "<string>",
"cancel": "<string>",
"canvas": "<string>"
},
"created_at": "<string>",
"started_at": "<string>",
"completed_at": "<string>",
"progress": {
"percent": 123,
"step": "<string>",
"message": "<string>"
},
"first_started_at": "<string>",
"input": {},
"result": {
"canvas_id": "<string>",
"canvas_url": "<string>",
"canvas_name": "<string>",
"conversation_id": "<string>",
"source_canvas_id": "<string>",
"source_canvas_url": "<string>",
"theme_canvas_id": "<string>",
"export": {
"url": "<string>",
"format": "<string>",
"status": "<string>",
"page_count": 123
}
},
"error": {},
"credits": {
"credits_used": 123,
"credits_remaining": 123
},
"retry_after_ms": 123
}{
"id": "task_01HT9WK8N3M2J4A5Z6P7Q8R9TV",
"kind": "design",
"status": "queued",
"attempt": 123,
"max_attempts": 123,
"attempts_started": 123,
"links": {
"self": "<string>",
"events": "<string>",
"cancel": "<string>",
"canvas": "<string>"
},
"created_at": "<string>",
"started_at": "<string>",
"completed_at": "<string>",
"progress": {
"percent": 123,
"step": "<string>",
"message": "<string>"
},
"first_started_at": "<string>",
"input": {},
"result": {
"canvas_id": "<string>",
"canvas_url": "<string>",
"canvas_name": "<string>",
"conversation_id": "<string>",
"source_canvas_id": "<string>",
"source_canvas_url": "<string>",
"theme_canvas_id": "<string>",
"export": {
"url": "<string>",
"format": "<string>",
"status": "<string>",
"page_count": 123
}
},
"error": {},
"credits": {
"credits_used": 123,
"credits_remaining": 123
},
"retry_after_ms": 123
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}{
"type": "invalid_request",
"code": "<string>",
"message": "<string>",
"doc_url": "<string>",
"request_id": "<string>",
"causes": [
"<unknown>"
],
"details": {},
"retry_after_ms": 123,
"retryable": true
}Authorizations
API key from Settings > Developer > REST API
Headers
Calendar-dated API version pin. New integrations should pin 2026-05-01 to opt into the newest response shapes. For back-compat the server also accepts requests with no header and resolves them to the current default (today: 2026-04-12); that default advances on each sunset date. Any unsupported value returns 400 unsupported_version.
2026-04-12, 2026-05-01 "2026-05-01"
Path Parameters
Prefixed task_ wire ID (Crockford base32 body) — the canonical, recommended form. For back-compat, a bare UUID string is also accepted in both path parameters and JSON request bodies (older integrations that stored raw UUIDs keep working), as is the prefix over a UUID body (task_00000000-0000-4000-8000-000000000000). All three are permanent, supported inputs; only the canonical form is ever emitted.
^task_[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$"task_01HT9WK8N3M2J4A5Z6P7Q8R9TV"
Response
Successful Response
Canonical wire-format for every async design operation.
All consumers -- REST, webhooks, SSE, MCP -- serialize through
Task.from_db() so the shape is always consistent.
Prefixed task_... identifier.
^task_[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$"task_01HT9WK8N3M2J4A5Z6P7Q8R9TV"
Discriminator for the kind-specific result payload.
design, export, remix, brand_kit_extract, layerize_assets Current lifecycle status.
queued, running, succeeded, failed, canceled, expired Current attempt number (1-based).
Maximum attempts before dead-lettering.
Number of attempts STARTED (claimed by a worker) — never attempts completed. 0 means the task has never been claimed. The claim itself increments the counter, so a task on its first attempt reports 1 while RUNNING and stays at 1 through that attempt's retry backoff. The retry signal is therefore status == "queued" && attempts_started >= 1 (a queued task that has already burned an attempt), NOT >= 2. Unlike attempt, this is the raw counter and is not floored to 1.
HATEOAS links for this task.
Show child attributes
Show child attributes
ISO 8601 timestamp.
ISO 8601 timestamp.
ISO 8601 timestamp.
Live progress for running tasks. Null when not applicable.
Show child attributes
Show child attributes
ISO 8601 timestamp of when the FIRST attempt was claimed. null means unknown — either the task has never started, or it predates this field being recorded. started_at remains the CURRENT attempt's start, so on a retried task the two differ and the gap is the real wall-clock age of the work.
Sanitized echo of the original request.
Result payload of a design or remix task.
Populated when status is succeeded, and partially populated for
some in-flight tasks (a remix surfaces its source canvas before the design
task finishes). Extra keys are permitted so synchronous-completion payloads
pass through without schema churn.
- TaskResult
- PublicLayerizeAssetsResult
Show child attributes
Show child attributes
Error info: {message, retryable, attempt}. Present for failed tasks, and also for a NON-terminal task that carries an error from an earlier attempt — on a non-terminal task this always describes the PREVIOUS, already-dead attempt, never the one currently in flight. attempt is the attempts_started value the error is tied to (0 when the failure happened before any claim).
Credit usage. Present only for completed tasks when billing is enabled.
Show child attributes
Show child attributes
Suggested milliseconds to wait before the next poll. Null for terminal tasks.