# 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](https://mynth.io/docs/concepts/tasks.md).

## Where each part comes from

| Call                         | Returns                                                                                               |
| ---------------------------- | ----------------------------------------------------------------------------------------------------- |
| A `POST` that creates a task | `task_id`, `estimated_cost`, sometimes `public_access_token`. See [create response](#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](https://mynth.io/docs/api-reference/webhook-payloads.md)  |

Poll `/status`, then read `/result`. Both are served from a cache and accept a
[`pat_` token](https://mynth.io/docs/api-reference/authentication.md#public-access-tokens).
`GET /tasks/{id}` reads the database and needs an API key.

## The object

```json
{
  "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](#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](#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](https://mynth.io/docs/api-reference/errors.md#task-failures)
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

`request` echoes the body after validation:

- Defaults are filled in, such as `model: "auto"` and `count: 1` on
  `image.generate`, or `effort: "high"` on `image.review`.
- Field names are `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.
- URLs in `webhook.custom` are cut down to `scheme://host`, because they
  often carry a token.

Each endpoint page under [Endpoints](https://mynth.io/docs/api-reference.md#endpoints) lists the
fields its task type accepts.

## Task types

| `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`

```json
{
  "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](https://mynth.io/docs/concepts/destinations.md) 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`

```json
{
  "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`

```json
{
  "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`

```json
{ "level": "sfw" }
```

`level` is `sfw` or `nsfw` by default, or one of your `value` strings with
custom levels.

### `image.alt`

```json
{ "alt": "A white lighthouse on dark rocks, lit by an orange sunset sky" }
```

`alt` is 1 to 160 characters.

### `image.review`

```json
{
  "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`

```json
{
  "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.

## Create response

Every `POST` that creates a task answers `201`:

```json
{
  "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](https://mynth.io/docs/api-reference/authentication.md#public-access-tokens) |

The media is never in this response. Wait for the task with
[polling](https://mynth.io/docs/guides/poll-for-results.md) or a [webhook](https://mynth.io/docs/concepts/webhooks.md).

## Listing tasks

`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 |

```json
{
  "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.

```ts
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.

## Status and result

```json
{ "data": { "status": "pending" } }
```

```json
{
  "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}`.

## Next steps

- [Tasks](https://mynth.io/docs/concepts/tasks.md): statuses, billing, retries, and file lifetimes.
- [Webhook payloads](https://mynth.io/docs/api-reference/webhook-payloads.md): the same result, pushed to you.
- [Errors](https://mynth.io/docs/api-reference/errors.md): the codes in `errors` and on failed items.
