# 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 | `taskId`, `estimatedCost`, sometimes `access`. 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           | `task.id`, `request`, and `result` or `errors`. 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": { "orderId": "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",
    "userId": "user_01JD8G3W1R5T6Y7U8I9O0P1Q2W",
    "apiKeyId": "ak_01KE7XWWEQ4MCGWKBQKJ1G47RP",
    "createdAt": "2026-07-04T10:00:00.000Z",
    "updatedAt": "2026-07-04T10:00:12.000Z"
  }
}
```

| Field       | Type                                 | Notes                                                                                 |
| ----------- | ------------------------------------ | ------------------------------------------------------------------------------------- |
| `id`        | string                               | `tsk_` + a ULID. Sorts by creation time                                               |
| `type`      | string                               | One of the six [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`                              |
| `userId`    | string                               | The account that owns the task                                                        |
| `apiKeyId`  | string \| `null`                     | The key that created it. `null` for dashboard and playground tasks                    |
| `createdAt` | ISO 8601 string                      | UTC                                                                                   |
| `updatedAt` | 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` |

A `completed` generation can still hold failed items. Check each entry in
`result.images` or `result.videos`. [Errors](https://mynth.io/docs/api-reference/errors.md#task-failures)
lists the codes.

Money is always a decimal string: `cost`, `estimatedCost`, 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`.
- Fields the schema does not know are gone. If a setting seems ignored, look
  here.
- `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 shortened to the scheme, host, and first 12
  characters of the path, 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.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.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",
      "audio": true
    }
  ]
}
```

| Field        | Type                                | Notes                                   |
| ------------ | ----------------------------------- | --------------------------------------- |
| `id`         | string                              | `vid_` + 32 random characters           |
| `url`        | string                              | Never `null`. Video has no destinations |
| `mynth_url`  | string                              | The same as `url` today                 |
| `cost`       | decimal string                      | What this video cost                    |
| `duration`   | number                              | Seconds                                 |
| `resolution` | `480p` \| `720p` \| `1080p` \| `4k` |                                         |
| `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": {
    "taskId": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP",
    "estimatedCost": "0.03",
    "access": { "publicAccessToken": "pat_eyJhbGciOiJIUzI1NiJ9..." }
  }
}
```

| Field                       | Notes                                                                                                                        |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `taskId`                    | Pass it to every task endpoint                                                                                               |
| `estimatedCost`             | USD held on your balance until the task settles. The final `cost` is normally equal or lower                                 |
| `access.publicAccessToken?` | Image generate, remove background, and video generate only. [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",
      "createdAt": "2026-07-04T10:00:00.000Z",
      "updatedAt": "2026-07-04T10:00:12.000Z"
    }
  ]
}
```

There is no `hasMore` 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.
