> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aihubmix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Image Generation

> Generate images synchronously or asynchronously with the native AIHubMix image protocol, query tasks, and download results.

<Note>
  [Model Schema API](/en/api/async-tasks#model-schema)
</Note>

## Quickstart

The native image endpoint is synchronous by default. Set the Boolean field `async` to `true` to create a background task. This example uses `qwen-image-2.0`.

<CodeGroup>
  ```bash Create task theme={null}
  curl -X POST https://aihubmix.com/ai/v1/images/generations \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "qwen-image-2.0",
      "prompt": "A flower shop with delicate windows, warm sunlight streaming in",
      "n": 1,
      "size": "1024x1024",
      "async": true
    }'
  ```

  ```json Create response theme={null}
  {
    "id": "task_01K0ABCDEF",
    "object": "image",
    "model": "qwen-image-2.0",
    "status": "in_progress",
    "output": [],
    "error": null,
    "created_at": 1784707200,
    "completed_at": null,
    "expires_at": null
  }
  ```

  ```bash Query task theme={null}
  curl https://aihubmix.com/ai/v1/images/{id} \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY"
  ```

  ```bash Download result theme={null}
  curl "{content_url}" \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
    --output result.png
  ```
</CodeGroup>

<h2 id="model-schema">
  How Do You Discover Async Media Models and Get Their Schemas?
</h2>

The discovery flow has two steps. First, retrieve text-to-image or text-to-video models that support the async APIs from the public model catalog. Then use a model's `model_id` to retrieve the request Schema for its endpoints.

<h3 id="async-media-model-list">
  List Models That Support the Async APIs
</h3>

The model catalog uses the same data source as Playground. Use `type=image_generation` for text-to-image models and `type=video` for text-to-video models. Adding `schema_checked=true` limits the list to models with a published and reviewed request Schema.

```bash theme={null}
# Text-to-image models
curl "https://aihubmix.com/api/v1/models?type=image_generation&schema_checked=true&sort_by=order"

# Text-to-video models
curl "https://aihubmix.com/api/v1/models?type=video&schema_checked=true&sort_by=order"
```

Both requests use the same endpoint. The `type` filter currently accepts one value, so request each model type separately.

The response has the shape `{success, message, data}`. The following fields in `data` are relevant to async media integrations.

| Field                     | Description                                                                        |
| ------------------------- | ---------------------------------------------------------------------------------- |
| `data[].model_id`         | Model ID used in generation requests and subsequent Schema requests                |
| `data[].model_name`       | Model display name                                                                 |
| `data[].types`            | Comma-separated model types; a model may include both `image_generation` and `llm` |
| `data[].input_modalities` | Comma-separated input modalities                                                   |
| `data[].schema_checked`   | `true` when the request Schema has been published and reviewed                     |

```json theme={null}
{
  "success": true,
  "message": "",
  "data": [
    {
      "model_id": "qwen-image-2.0",
      "model_name": "Qwen Image 2.0",
      "types": "image_generation",
      "input_modalities": "text,image",
      "schema_checked": true
    }
  ]
}
```

<h3 id="single-model-schema">
  Get the Request Schema for One Model
</h3>

Supported fields, enumerations, and numeric ranges can vary by model. Before submitting an image or video request, use the public endpoint below to retrieve the available endpoints and request JSON Schemas for the selected model.

```bash theme={null}
# Text-to-image model
curl "https://aihubmix.com/call/schema/models/qwen-image-2.0/endpoints"

# Text-to-video model
curl "https://aihubmix.com/call/schema/models/wan2.6-t2v/endpoints"
```

The response has a `modality` of `image` or `video`. Each item in the `endpoints` array describes one available calling protocol.

| Field                        | Description                                                                                               |
| ---------------------------- | --------------------------------------------------------------------------------------------------------- |
| `default_endpoint`           | Default endpoint identifier for the model                                                                 |
| `endpoints[].endpoint`       | Endpoint identifier                                                                                       |
| `endpoints[].method`         | HTTP method, such as `POST`                                                                               |
| `endpoints[].path`           | Request path                                                                                              |
| `endpoints[].content_types`  | Content types accepted by the endpoint                                                                    |
| `endpoints[].lifecycle`      | Sync or async mode, polling path, and status values                                                       |
| `endpoints[].request.schema` | Complete request JSON Schema for this model and endpoint, including required fields and value constraints |

A model may return both `/ai/v1` endpoints and OpenAI-compatible `/v1` endpoints. OpenAI-compatible endpoints may not yet support the latest models, so prefer the `/ai/v1` endpoints. For async task APIs, select the item whose `path` is `/ai/v1/images/generations` or `/ai/v1/videos`, then use its `request.schema`. Do not depend on the position of an item in the `endpoints` array.

The following commands extract the request Schema for each async task endpoint.

```bash theme={null}
# Text-to-image
curl -s "https://aihubmix.com/call/schema/models/qwen-image-2.0/endpoints" \
  | jq '.endpoints[] | select(.path == "/ai/v1/images/generations") | .request.schema'

# Text-to-video
curl -s "https://aihubmix.com/call/schema/models/wan2.6-t2v/endpoints" \
  | jq '.endpoints[] | select(.path == "/ai/v1/videos") | .request.schema'
```

The endpoint returns `404 model_not_found` when the model does not exist or has no discoverable endpoints. It returns `500 endpoints_unavailable` when endpoint data is temporarily unavailable.

***

<h2 id="create-async-task">
  How Do You Create Image Tasks?
</h2>

<h3 id="create-sync-image">
  Synchronous Images
</h3>

When `async` is omitted or set to `false`, the endpoint waits for generation to finish and returns a task object:

```bash theme={null}
curl -X POST https://aihubmix.com/ai/v1/images/generations \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen-image-2.0",
    "prompt": "A flower shop with delicate windows, warm sunlight streaming in",
    "n": 1,
    "size": "1024x1024",
    "response_format": "url"
  }'
```

Synchronous image tasks also save a task record. If the client connection is interrupted or the creation response is lost, use `GET /ai/v1/images` to find the corresponding task.

<h3 id="create-async-image">
  Asynchronous Images
</h3>

When `async` is set to the Boolean value `true`, the endpoint returns a task object immediately and generation continues in the background:

```bash theme={null}
curl -X POST https://aihubmix.com/ai/v1/images/generations \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen-image-2.0",
    "prompt": "A flower shop with delicate windows, warm sunlight streaming in",
    "n": 2,
    "size": "1024x1024",
    "async": true
  }'
```

Use `GET /ai/v1/images/{id}` to query an asynchronous image task. After completion, request the `content_url` from each `output` item directly. The URL already contains the corresponding image `result_id`.

<Note>
  `async` in an image request must be a Boolean. `webhook_url` and
  `webhook_events_filter` can only be used with `async: true`.
</Note>

<h3 id="image-parameters">
  Standard Image Fields
</h3>

| Field                   | Type          | Required | Description                                                         |
| ----------------------- | ------------- | -------- | ------------------------------------------------------------------- |
| `model`                 | string        | Yes      | Model name                                                          |
| `prompt`                | string        | Yes      | Image description; must not be empty                                |
| `n`                     | integer/null  | No       | Number of images, minimum `1`, default `1`                          |
| `size`                  | string/null   | No       | `{width}x{height}`, for example `1024x1024`                         |
| `aspect_ratio`          | string/null   | No       | Aspect ratio; mutually exclusive with `size`                        |
| `seed`                  | integer/null  | No       | Random seed                                                         |
| `negative_prompt`       | string/null   | No       | Negative prompt                                                     |
| `image`                 | string/object | No       | One input image as a URL, Data URI, Base64 value, or `{url}` object |
| `images`                | array/null    | No       | Multiple input images                                               |
| `mask`                  | string/object | No       | Image-editing mask                                                  |
| `output_format`         | string/null   | No       | `png`, `jpeg`, or `webp`; default `png`                             |
| `response_format`       | string/null   | No       | `url` or `b64_json`; default `url`                                  |
| `async`                 | boolean       | No       | Set to `true` for asynchronous execution                            |
| `webhook_url`           | string        | No       | HTTPS callback URL, up to 512 characters                            |
| `webhook_events_filter` | string\[]     | No       | A non-empty subset of `completed`, `failed`, and `cancelled`        |
| `extra`                 | object/null   | No       | Model-specific extension parameters                                 |

***

<h2 id="task-object">
  Media Task Object
</h2>

Image and video endpoints return the following structure:

```json theme={null}
{
  "id": "task_01K0ABCDEF",
  "object": "image",
  "model": "qwen-image-2.0",
  "status": "completed",
  "output": [
    {
      "index": 0,
      "type": "file",
      "b64_json": null,
      "content_url": "https://aihubmix.com/ai/v1/images/task_01K0ABCDEF/content/result_01K0XYZ"
    }
  ],
  "error": null,
  "created_at": 1784707200,
  "completed_at": 1784707320,
  "expires_at": null
}
```

| Field          | Type         | Description                                                                 |
| -------------- | ------------ | --------------------------------------------------------------------------- |
| `id`           | string       | Platform task ID                                                            |
| `object`       | string       | `image` or `video`                                                          |
| `model`        | string       | Model name                                                                  |
| `status`       | string       | Current task status                                                         |
| `output`       | array        | Generated results; an empty array when no result is available               |
| `error`        | object/null  | Failure details, which may contain `code`, `message`, and `upstream_detail` |
| `created_at`   | integer      | Creation time in Unix seconds                                               |
| `completed_at` | integer/null | Final-status time in Unix seconds                                           |
| `expires_at`   | null         | Media-specific endpoints currently return `null`                            |

Media `output` item:

| Field         | Type        | Description                     |
| ------------- | ----------- | ------------------------------- |
| `index`       | integer     | Result order, starting from `0` |
| `type`        | string      | Currently `file`                |
| `content_url` | string/null | Result download URL             |
| `b64_json`    | string/null | Base64-encoded image result     |

<h3 id="task-status">
  Status Reference
</h3>

| Status        | Final | Description                                                 |
| ------------- | ----- | ----------------------------------------------------------- |
| `pending`     | No    | The platform has accepted the task and it is waiting to run |
| `in_progress` | No    | The task is running                                         |
| `completed`   | Yes   | The task is complete and `output` is available              |
| `failed`      | Yes   | The task failed; see `error` for the reason                 |
| `cancelled`   | Yes   | The task was cancelled                                      |

Clients can query every 15 seconds until the status becomes `completed`, `failed`, or `cancelled`. The 15-second interval is a client polling recommendation, not a server protocol limit.

***

<h2 id="query-tasks">
  How Do You Query Media Tasks?
</h2>

<h3 id="query-task-detail">
  Get Media Details
</h3>

```bash theme={null}
# Image
curl https://aihubmix.com/ai/v1/images/{task_id} \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"

# Video
curl https://aihubmix.com/ai/v1/images/{task_id} \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

Media detail endpoints can return an updated task status, so use the corresponding image or video detail endpoint for media polling.

<h3 id="query-task-list">
  List Media Tasks
</h3>

If the creation response is lost, use the corresponding media list to recover the task ID:

```bash theme={null}
curl "https://aihubmix.com/ai/v1/images?limit=20&order=desc" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

| Parameter | Type    | Default | Description                                                       |
| --------- | ------- | ------- | ----------------------------------------------------------------- |
| `after`   | string  | -       | Pagination cursor; use `next_after` from the previous page        |
| `limit`   | integer | `20`    | Number of items per page, maximum `100`                           |
| `order`   | string  | `desc`  | `asc` for ascending order; all other values are treated as `desc` |

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "task_01K0ABCDEF",
      "object": "image",
      "model": "qwen-image-2.0",
      "status": "in_progress",
      "output": [],
      "error": null,
      "created_at": 1784707200,
      "completed_at": null,
      "expires_at": null
    }
  ],
  "has_more": true,
  "next_after": "task_01K0ABCDEF"
}
```

Media lists return task snapshots at query time and do not actively update task status.

***

<h2 id="unified-tasks">
  How Do You Use the Unified Task API?
</h2>

The unified task API supports the following filters:

```bash theme={null}
curl "https://aihubmix.com/ai/v1/tasks?object=image&status=in_progress&limit=20&order=desc" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

| Parameter | Type    | Default | Description                                                     |
| --------- | ------- | ------- | --------------------------------------------------------------- |
| `object`  | string  | -       | `llm`, `image`, or `video`                                      |
| `status`  | string  | -       | `pending`, `in_progress`, `completed`, `failed`, or `cancelled` |
| `model`   | string  | -       | Exact filter by model name                                      |
| `after`   | string  | -       | Pagination cursor                                               |
| `limit`   | integer | `20`    | Range from `1` to `100`                                         |
| `order`   | string  | `desc`  | `asc` or `desc`                                                 |

Unified task details:

```bash theme={null}
curl https://aihubmix.com/ai/v1/tasks/{task_id} \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

A unified `output` item for a media task:

```json theme={null}
{
  "index": 0,
  "result_id": "result_01K0XYZ",
  "type": "file",
  "content_type": "image/png",
  "content_url": "https://aihubmix.com/ai/v1/tasks/task_01K0ABCDEF/content/result_01K0XYZ"
}
```

For a single-artifact task, request `/ai/v1/tasks/{id}/content` directly. For a multi-artifact task, request `/ai/v1/tasks/{id}/content/{result_id}`. Omitting the result ID returns `400 result_id_required`.

<Note>
  Unified task list, detail, and content endpoints are isolated by the Bearer
  token used to create the task. Other API Keys under the same account cannot
  read the task.
</Note>

***

<h2 id="get-task-results">
  How Do You Download Media Results?
</h2>

<h3 id="multiple-artifacts">
  Download Images
</h3>

After an image task is completed, request `output[].content_url` from the media task object for each result:

```bash theme={null}
curl "{content_url}" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  --output result.png
```

The media download path for images is `/ai/v1/images/{id}/content/{result_id}`. The media task object does not expose `result_id` separately, so clients can use `content_url` directly.

When `b64_json` is not empty, decode that field directly from Base64.

<h2 id="webhooks">
  How Do You Use Webhooks?
</h2>

Asynchronous images and videos support task-level webhooks:

```bash theme={null}
curl -X POST https://aihubmix.com/ai/v1/images/generations \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen-image-2.0",
    "prompt": "A tranquil garden at sunrise",
    "n": 1,
    "async": true,
    "size": "1024x1024",
    "webhook_url": "https://example.com/webhooks/aihubmix",
    "webhook_events_filter": ["completed", "failed"]
  }'
```

`webhook_url` can be up to 512 characters and cannot point to localhost, private networks, or other restricted addresses. When `webhook_events_filter` is omitted, the platform sends `completed`, `failed`, and `cancelled`. When explicitly provided, the array must not be empty or contain duplicates, and it must be used with `webhook_url`.

When `webhook_url` is omitted from the request, asynchronous image and video tasks try to use the default callback URL configured for the account. An invalid account default is ignored and does not prevent task creation.

<h3 id="webhook-payload">
  Callback Request
</h3>

```json theme={null}
{
  "event_id": "evt_01K0ABCDEF",
  "event_type": "completed",
  "created_at": "2026-08-12T12:00:00Z",
  "data": {
    "task_id": "task_01K0ABCDEF",
    "status": "completed",
    "model": "qwen-image-2.0",
    "results": [
      {
        "url": "https://aihubmix.com/ai/v1/tasks/task_01K0ABCDEF/content/result_01K0XYZ"
      }
    ]
  }
}
```

| Field                | Description                                       |
| -------------------- | ------------------------------------------------- |
| `event_id`           | Callback event ID, used for deduplication         |
| `event_type`         | `completed`, `failed`, or `cancelled`             |
| `created_at`         | Event time in RFC 3339 format                     |
| `data.task_id`       | Platform task ID                                  |
| `data.status`        | Current final status                              |
| `data.model`         | Model name                                        |
| `data.results[].url` | Result download URL, when available on completion |
| `data.error`         | Error details, when available on failure          |

`results` appears only when results have been archived. Downloads still require a Bearer token.

<h3 id="webhook-retry">
  Retries and Deduplication
</h3>

The platform uses at-least-once delivery, so the same event may be delivered more than once:

* HTTP `2xx` indicates successful receipt.
* HTTP `5xx`, network errors, or timeouts trigger a retry.
* HTTP `3xx` and `4xx` are not retried.
* Up to 6 delivery attempts are made, with retry intervals of 1, 4, 16, 64, and 256 seconds.

The receiver should store `event_id` and return `2xx` immediately when the same event is received again.

<Warning>
  Task-level webhooks do not include a separate signing secret. If signature
  verification is required, configure an account-level webhook subscription and
  retain task detail queries as the method for confirming results.
</Warning>

***

<h2 id="error-codes">
  Error Responses and Error Codes
</h2>

```json theme={null}
{
  "error": {
    "message": "Task not found.",
    "type": "invalid_request_error",
    "code": "task_not_found",
    "tid": "req_01K0ABCDEF"
  }
}
```

| HTTP status | Error code                      | Description                                                                                  |
| ----------- | ------------------------------- | -------------------------------------------------------------------------------------------- |
| 400         | `invalid_request`               | Incorrect parameter type or value                                                            |
| 400         | `result_id_required`            | The unified task has multiple results, but `result_id` was omitted from the download request |
| 400         | `webhook_invalid`               | The webhook URL is invalid, or the filter is provided without a URL                          |
| 400         | `webhook_events_filter_invalid` | The webhook event list is invalid                                                            |
| 401         | `authentication_failed`         | The API Key is missing or invalid                                                            |
| 403         | `async_not_enabled`             | Async tasks are not enabled for the account                                                  |
| 404         | `task_not_found`                | The task or pagination cursor does not exist                                                 |
| 404         | `result_not_found`              | The result does not exist or cannot currently be downloaded                                  |
| 410         | `artifact_expired`              | The result has expired                                                                       |
| 413         | `request_too_large`             | The request body exceeds 32 MiB                                                              |
| 429         | `too_many_downloads`            | The result download limit has been exceeded                                                  |
| 503         | `async_unavailable`             | The asynchronous image service is temporarily unavailable                                    |

`error.tid` is the request trace ID. Include it when contacting technical support for troubleshooting.

***

## Complete example

These examples create an asynchronous task, poll it, and save every returned image.

<CodeGroup>
  ```python Python theme={null}
  import base64
  import os
  import time

  import requests

  base_url = "https://aihubmix.com"
  headers = {
      "Authorization": f"Bearer {os.environ['AIHUBMIX_API_KEY']}",
      "Content-Type": "application/json",
  }

  response = requests.post(
      f"{base_url}/ai/v1/images/generations",
      headers=headers,
      json={
          "model": "qwen-image-2.0",
          "prompt": "A flower shop with delicate windows, warm sunlight streaming in",
          "n": 1,
          "size": "1024x1024",
          "async": True,
      },
      timeout=60,
  )
  response.raise_for_status()
  task = response.json()

  while task["status"] not in {"completed", "failed", "cancelled"}:
      time.sleep(15)
      response = requests.get(
          f"{base_url}/ai/v1/images/{task['id']}",
          headers=headers,
          timeout=30,
      )
      response.raise_for_status()
      task = response.json()

  if task["status"] != "completed":
      raise RuntimeError(task.get("error") or task["status"])

  for output in task["output"]:
      filename = f"result-{output['index']}.png"
      if output.get("b64_json"):
          content = base64.b64decode(output["b64_json"])
      else:
          result = requests.get(output["content_url"], headers=headers, timeout=120)
          result.raise_for_status()
          content = result.content
      with open(filename, "wb") as file:
          file.write(content)
  ```

  ```typescript TypeScript theme={null}
  import { writeFile } from "node:fs/promises";

  const baseUrl = "https://aihubmix.com";
  const headers = {
    Authorization: `Bearer ${process.env.AIHUBMIX_API_KEY}`,
    "Content-Type": "application/json",
  };

  const created = await fetch(`${baseUrl}/ai/v1/images/generations`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: "qwen-image-2.0",
      prompt: "A flower shop with delicate windows, warm sunlight streaming in",
      n: 1,
      size: "1024x1024",
      async: true,
    }),
  });
  if (!created.ok) throw new Error(await created.text());
  let task = await created.json();

  const finished = new Set(["completed", "failed", "cancelled"]);
  while (!finished.has(task.status)) {
    await new Promise((resolve) => setTimeout(resolve, 15_000));
    const polled = await fetch(`${baseUrl}/ai/v1/images/${task.id}`, { headers });
    if (!polled.ok) throw new Error(await polled.text());
    task = await polled.json();
  }

  if (task.status !== "completed") {
    throw new Error(JSON.stringify(task.error ?? task.status));
  }

  for (const output of task.output) {
    let content;
    if (output.b64_json) {
      content = Buffer.from(output.b64_json, "base64");
    } else {
      const result = await fetch(output.content_url, { headers });
      if (!result.ok) throw new Error(await result.text());
      content = Buffer.from(await result.arrayBuffer());
    }
    await writeFile(`result-${output.index}.png`, content);
  }
  ```
</CodeGroup>
