The task object
Every field of a task, the result shape for each task type, and what the create, list, status, and result endpoints return.
Every generation and analysis call creates a task, and every task endpoint returns some part of this object. The lifecycle, billing, and file lifetimes are on tasks.
| Call | Returns |
|---|---|
A POST that creates a task | task_id, estimated_cost, sometimes public_access_token. See create response |
GET /tasks | A page of rows: id, type, status, cost, timestamps |
GET /tasks/{id} | The full object |
GET /tasks/{id}/status | status only |
GET /tasks/{id}/result | id, type, status, result |
| A webhook delivery | The full object, as the event's data. See webhook payloads |
Poll /status, then read /result. Both are served from a cache and accept a
pat_ token.
GET /tasks/{id} reads the database and needs an API key.
{
"data": {
"id": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP",
"type": "image.generate",
"status": "completed",
"request": {
"model": "black-forest-labs/flux.2-pro",
"prompt": "A lighthouse at dusk, film grain",
"count": 1,
"metadata": { "order_id": "ord_42" }
},
"result": {
"model": "black-forest-labs/flux.2-pro",
"images": [
{
"status": "success",
"id": "img_V1StGXR8Z5jdHi6BmyT0sC1pQ2rN4wLk",
"url": "https://cdn.mynth.io/images/img_V1StGXR8Z5jdHi6BmyT0sC1pQ2rN4wLk.webp",
"mynth_url": "https://cdn.mynth.io/images/img_V1StGXR8Z5jdHi6BmyT0sC1pQ2rN4wLk.webp",
"size": "1536x1024",
"format": "webp"
}
]
},
"errors": null,
"cost": "0.03000000",
"user_id": "user_01JD8G3W1R5T6Y7U8I9O0P1Q2W",
"api_key_id": "ak_01KE7XWWEQ4MCGWKBQKJ1G47RP",
"created_at": "2026-07-04T10:00:00.000Z",
"updated_at": "2026-07-04T10:00:12.000Z"
}
}| Field | Type | Notes |
|---|---|---|
id | string | tsk_ + a ULID. Sorts by creation time |
type | string | One of the seven task types. Decides the shape of request and result |
status | pending | completed | failed | pending covers queued and running. A settled task never changes again |
request | object | The body as accepted, with defaults filled in. See request |
result | object | null | Set only when completed. Shape depends on type |
errors | [{ code, message? }] | null | Set only when failed |
cost | decimal string | null | USD charged, 8 decimal places. Set only when completed |
user_id | string | The account that owns the task |
api_key_id | string | null | The key that created it. null for dashboard and playground tasks |
created_at | ISO 8601 string | UTC |
updated_at | ISO 8601 string | UTC. The last change |
The combinations are fixed:
status | result | errors | cost |
|---|---|---|---|
pending | null | null | null |
completed | set | null | set |
failed | null | at least one entry | null |
completed means the task finished and has a result. It does not mean
every item succeeded: a generation can complete with failed images or videos,
so check each entry's status in result.images or result.videos. failed
means the task as a whole failed: there is no result, nothing is charged,
and errors says why. Errors
lists the codes.
Money is always a decimal string: cost, estimated_cost, and a video's own
cost. Add amounts up with a decimal type, not floats.
request echoes the body after validation:
model: "auto" and count: 1 on
image.generate, or effort: "high" on image.review.snake_case, the same as you send them. A body with a
field the endpoint does not define is rejected, so nothing is dropped.metadata comes back unchanged, here and in every webhook. Put your own ids
in it to match a task to your records.webhook.custom are cut down to scheme://host, because they
often carry a token.Each endpoint page under Endpoints lists the fields its task type accepts.
type | Created by | result holds |
|---|---|---|
image.generate | POST /image/generate | model, images[], magic_prompt? |
image.remove_background | POST /image/remove-background | image |
image.upscale | POST /image/upscale | image |
image.rate | POST /image/rate | level |
image.alt | POST /image/alt | alt |
image.review | POST /image/review | score, summary, findings, strengths |
video.generate | POST /video/generate | model, videos[] |
Fields marked ? are absent, not null, when they do not apply.
image.generate{
"model": "black-forest-labs/flux.2-pro",
"images": [
{
"status": "success",
"id": "img_V1StGXR8Z5jdHi6BmyT0sC1pQ2rN4wLk",
"url": "https://assets.example.com/renders/img_V1StGXR8Z5jdHi6BmyT0sC1pQ2rN4wLk.webp",
"mynth_url": "https://cdn.mynth.io/images/img_V1StGXR8Z5jdHi6BmyT0sC1pQ2rN4wLk.webp",
"size": "1536x1024",
"format": "webp",
"rating": { "status": "success", "level": "sfw" }
},
{
"status": "failed",
"error": {
"code": "restricted_content",
"message": "The request was blocked by content moderation."
}
}
],
"magic_prompt": { "positive": "A lone lighthouse on a rocky shore at dusk, ..." }
}| Field | Type | Notes |
|---|---|---|
model | string | The model that ran. With auto, the one Mynth picked |
images | array | One entry per requested image, each success or failed |
magic_prompt? | { positive, negative? } | The rewritten prompt, when the request set magic_prompt: true |
A successful image:
| Field | Type | Notes |
|---|---|---|
status | "success" | |
id | string | img_ + 32 random characters |
url | string | null | Your storage when a destination was named, else the same as mynth_url. null if that upload failed |
mynth_url | string | Mynth's copy. Always set, served for 7 days |
size | string | Measured from the file, {width}x{height} |
format | png | jpg | webp | What output.format asked for, or what the provider returned |
rating? | object | When the request set rating: { status: "success", level } or { status: "failed", error: { code } } |
A failed image is { "status": "failed", "error": { "code", "message?" } }
with nothing else, and is not charged. Generated images carry no
destination block, so a failed upload shows only as url: null.
image.remove_background{
"image": {
"id": "img_Q8mZr2LkT0vWc5NhY7pD3xFa9GsJ1bEu",
"url": "https://cdn.mynth.io/images/img_Q8mZr2LkT0vWc5NhY7pD3xFa9GsJ1bEu.png",
"mynth_url": "https://cdn.mynth.io/images/img_Q8mZr2LkT0vWc5NhY7pD3xFa9GsJ1bEu.png",
"size": "1024x1024",
"format": "png"
}
}image has the fields of a successful image.generate entry, without
status and rating. format is png or webp. There is no per-item
failure: if the work fails, the task is failed.
When the request named a destination, image also carries destination:
{ status: "success", name }, or
{ status: "failed", name, error: { code, message?, provider_response? } }
next to url: null.
image.upscale{
"image": {
"id": "img_Hn4cW8pLx2RsT6vYb0QjM9kE3dAf7ZuG",
"url": "https://cdn.mynth.io/images/img_Hn4cW8pLx2RsT6vYb0QjM9kE3dAf7ZuG.png",
"mynth_url": "https://cdn.mynth.io/images/img_Hn4cW8pLx2RsT6vYb0QjM9kE3dAf7ZuG.png",
"size": "3072x4608",
"format": "png"
}
}Same shape as image.remove_background, with destination under the same
rules. format is png, jpg, or webp. size is what the provider
returned, which can differ slightly from the source times the factor.
image.rate{ "level": "sfw" }level is sfw or nsfw by default, or one of your value strings with
custom levels.
image.alt{ "alt": "A white lighthouse on dark rocks, lit by an orange sunset sky" }alt is 1 to 160 characters.
image.review{
"score": 3,
"summary": "Clean composition with one visible defect in the left hand.",
"findings": [
{
"finding": "The left hand has six fingers",
"category": "anatomy",
"severity": "major",
"where": "Bottom left, the hand resting on the railing",
"confidence": "high"
}
],
"strengths": [{ "strength": "Warm, consistent lighting", "confidence": "medium" }]
}| Field | Type | Notes |
|---|---|---|
score | number, 1 to 4 | Median of the reviewers' scores. Higher is better |
summary | string | A short prose summary |
findings[].category | string | Usually anatomy, text, composition, artifact, color, or lighting. An open set |
findings[].severity | critical | major | minor | |
findings[].confidence | low | medium | high | Reviewer agreement: bare threshold, majority, strong consensus |
strengths[] | { strength, confidence } | Same confidence scale |
video.generate{
"model": "bytedance/seedance-2.0-mini",
"videos": [
{
"status": "success",
"id": "vid_3HkP9wQz7LmR2tXc8VbN5dFy0JsA4gUe",
"url": "https://cdn.mynth.io/videos/vid_3HkP9wQz7LmR2tXc8VbN5dFy0JsA4gUe.mp4",
"mynth_url": "https://cdn.mynth.io/videos/vid_3HkP9wQz7LmR2tXc8VbN5dFy0JsA4gUe.mp4",
"cost": "0.40500000",
"duration": 5,
"resolution": "720p",
"aspect_ratio": "9:16",
"audio": true
}
]
}| Field | Type | Notes |
|---|---|---|
id | string | vid_ + 32 random characters |
url | string | null | The same as mynth_url today. Nullable, as on images, so video destinations can be added without a breaking change. Write url ?? mynth_url |
mynth_url | string | Mynth's copy. Always set |
cost | decimal string | What this video cost |
duration | number | Seconds |
resolution | 480p | 720p | 1080p | 4k | |
aspect_ratio | string | null | The ratio it was rendered at, such as 9:16. null when a first frame set the shape |
audio | boolean | Whether the video has generated audio |
A failed video is { "status": "failed", "error": { "code", "message?" } },
like a failed image.
Every POST that creates a task answers 201:
{
"data": {
"task_id": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP",
"estimated_cost": "0.03",
"public_access_token": "pat_eyJhbGciOiJIUzI1NiJ9..."
}
}| Field | Notes |
|---|---|
task_id | Pass it to every task endpoint |
estimated_cost | USD held on your balance until the task settles. The final cost is normally equal or lower |
public_access_token? | Only when the request set generate_public_access_token: true, on image generate, remove background, upscale, and video generate. Rules |
The media is never in this response. Wait for the task with polling or a webhook.
GET /tasks returns the calling key's tasks, newest first. Rows omit
request and result, so fetch a task by id for those.
| Query | Default | Notes |
|---|---|---|
limit | 20 | 1 to 100 |
after | none | A task id. Returns tasks older than it |
{
"data": [
{
"id": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP",
"type": "image.generate",
"status": "completed",
"cost": "0.03000000",
"created_at": "2026-07-04T10:00:00.000Z",
"updated_at": "2026-07-04T10:00:12.000Z"
}
]
}There is no has_more and no total. To page, pass the last id you received
as after. A page shorter than limit is the last one.
let after: string | undefined;
do {
const url = new URL("https://api.mynth.io/tasks");
url.searchParams.set("limit", "100");
if (after) url.searchParams.set("after", after);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.MYNTH_API_KEY}` },
});
const { data } = await res.json();
for (const task of data) console.log(task.id, task.status, task.cost);
after = data.length === 100 ? data.at(-1).id : undefined;
} while (after);After a create request times out, look here before resending. If the task is listed, it was created, and sending the request again would create and charge a second one.
{ "data": { "status": "pending" } }{
"data": {
"id": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP",
"type": "image.alt",
"status": "completed",
"result": { "alt": "A white lighthouse on dark rocks, lit by an orange sunset sky" }
}
}/result answers 200 while the task is pending, with result: null. It
never includes errors, so for a failed task read GET /tasks/{id}.
errors and on failed items.